H5ad generation

This commit is contained in:
Flash 2025-07-27 18:10:06 +08:00
parent e157a17d69
commit b39288fb9f
16 changed files with 51 additions and 24 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
*_marker_genes.txt
*_scaled.csv

BIN
embryo-backend/Data/CS11.h5ad (Stored with Git LFS)

Binary file not shown.

BIN
embryo-backend/Data/CS12.h5ad (Stored with Git LFS)

Binary file not shown.

BIN
embryo-backend/Data/CS13.h5ad (Stored with Git LFS)

Binary file not shown.

BIN
embryo-backend/Data/CS14.h5ad (Stored with Git LFS)

Binary file not shown.

BIN
embryo-backend/Data/CS15.h5ad (Stored with Git LFS)

Binary file not shown.

BIN
embryo-backend/Data/CS16.h5ad (Stored with Git LFS)

Binary file not shown.

BIN
embryo-backend/Data/CS17.h5ad (Stored with Git LFS)

Binary file not shown.

BIN
embryo-backend/Data/CS18.h5ad (Stored with Git LFS)

Binary file not shown.

BIN
embryo-backend/Data/CS20.h5ad (Stored with Git LFS)

Binary file not shown.

BIN
embryo-backend/Data/CS21.h5ad (Stored with Git LFS)

Binary file not shown.

BIN
embryo-backend/Data/CS22.h5ad (Stored with Git LFS)

Binary file not shown.

BIN
embryo-backend/Data/CS23.h5ad (Stored with Git LFS)

Binary file not shown.

View File

@ -19,7 +19,7 @@ assert os.path.exists(gene_list_file), f"❌ 缺少基因名文件 {gene_list_fi
all_gene_names = pd.read_csv(gene_list_file, header=None)[0].tolist()
gene_names = all_gene_names[:n_genes]
# ==== 真实 marker 基因 ====
# ==== marker 基因 ====
true_markers = {
"Ectoderm": ["SOX2", "PAX6", "NES", "TUBB3", "OTX2"],
"Mesoderm": ["TBXT", "MESP1", "HAND1", "GATA4", "PDGFRA"],
@ -30,6 +30,7 @@ true_markers = {
# ==== 采样函数 ====
def sample_mesh(mesh, label, n_samples):
"""采样点云 + 颜色 + 标签"""
if use_surface_sampling:
points, face_idx = trimesh.sample.sample_surface(mesh, n_samples)
if hasattr(mesh.visual, "vertex_colors") and len(mesh.visual.vertex_colors) == len(mesh.vertices):
@ -63,7 +64,7 @@ for glb_name in glb_files:
scene = trimesh.load(glb_path)
if isinstance(scene, trimesh.Scene):
for geom in scene.geometry.values():
all_bounds.append(geom.bounds) # (min,max)
all_bounds.append(geom.bounds)
else:
all_bounds.append(scene.bounds)
@ -73,7 +74,7 @@ global_max = np.max(all_bounds[:,1,:], axis=0)
global_size = np.max(global_max - global_min)
print(f"🌍 统一缩放基准: global_size={global_size}")
# ==== Step 2: 处理单个 GLB ====
# ==== Step 2: 处理单个 GLB(应用全局变换) ====
def process_glb(glb_path, sample_name):
scene = trimesh.load(glb_path)
all_points, all_colors, all_labels = [], [], []
@ -83,7 +84,21 @@ def process_glb(glb_path, sample_name):
for name, geom in scene.geometry.items():
ratio = len(geom.vertices) / total_points
n_samples = max(1, int(N_total * ratio))
p, c, l = sample_mesh(geom, name, n_samples)
# ✅ 保护性获取变换矩阵
try:
transform = scene.graph.get(name)[0]
except Exception:
print(f"⚠️ {sample_name}: 子网格 {name} 没有变换路径,使用单位矩阵")
transform = np.eye(4)
# ✅ 应用全局变换
verts_world = trimesh.transform_points(geom.vertices, transform)
# ✅ 生成全局 mesh 进行采样
mesh_world = trimesh.Trimesh(vertices=verts_world, faces=geom.faces, process=False)
p, c, l = sample_mesh(mesh_world, name, n_samples)
all_points.append(p)
all_colors.append(c)
all_labels.append(l)
@ -101,26 +116,21 @@ def process_glb(glb_path, sample_name):
idx = np.random.choice(len(points), N_total, replace=False)
points, colors, labels = points[idx], colors[idx], labels[idx]
# ✅ 平移到中心点
# ✅ 一次性中心化 + 统一缩放
center = points.mean(axis=0)
points_centered = points - center
# ✅ 统一缩放(保持原始比例)
points_scaled = points_centered / global_size
# ✅ 如需修正坐标系方向可启用以下行示例交换Y和Z
# points_scaled = points_scaled[:, [0, 2, 1]]
# points_scaled[:, 1] *= -1
df = pd.DataFrame(points_scaled, columns=["x","y","z"])
df["r"], df["g"], df["b"] = colors[:,0], colors[:,1], colors[:,2]
df["label"] = labels
csv_file = os.path.join(script_dir, f"{sample_name}_point_cloud_30000_centered_scaled.csv")
df.to_csv(csv_file, index=False)
print(f"✅ 已导出 {csv_file} (中心化 & 统一大小, 保持比例)")
print(f"✅ 已导出 {csv_file} (修正组织错位, 中心化 & 统一比例)")
return df
# ==== Step 3: 生成 h5ad ====
def create_h5ad(df, sample_name):
points = df[['x','y','z']].to_numpy()

View File

@ -9,4 +9,5 @@ dependencies = [
"flask>=3.1.1",
"flask-cors>=6.0.1",
"pandas>=2.3.1",
"trimesh>=4.7.1",
]

14
uv.lock
View File

@ -100,6 +100,7 @@ dependencies = [
{ name = "flask" },
{ name = "flask-cors" },
{ name = "pandas" },
{ name = "trimesh" },
]
[package.metadata]
@ -108,6 +109,7 @@ requires-dist = [
{ name = "flask", specifier = ">=3.1.1" },
{ name = "flask-cors", specifier = ">=6.0.1" },
{ name = "pandas", specifier = ">=2.3.1" },
{ name = "trimesh", specifier = ">=4.7.1" },
]
[[package]]
@ -421,6 +423,18 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" },
]
[[package]]
name = "trimesh"
version = "4.7.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "numpy" },
]
sdist = { url = "https://files.pythonhosted.org/packages/95/05/50656189ebd0563b2130ddd9b609b1db99eb241687dac0d2585882c35c33/trimesh-4.7.1.tar.gz", hash = "sha256:3863c2b2281fc7a99bf0b5de4a0011229bde4663babc0c1b53a1f855149ec898", size = 800778, upload-time = "2025-07-16T20:24:50.248Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/c6/64/99e785dd4bb5796881b5686b0869a6ebf75b1b7d00932af577eb343c4f72/trimesh-4.7.1-py3-none-any.whl", hash = "sha256:338c938ae78ad5b4d08dd6ceaa739498a47627bf0073147ee9a384ddd7435267", size = 709034, upload-time = "2025-07-16T20:24:47.256Z" },
]
[[package]]
name = "typing-extensions"
version = "4.14.1"