Matycaba2016/Biohub / src /dataset.py
Matycaba2016's picture
download
raw
5.7 kB
import os, glob, json
import numpy as np
import torch
from torch.utils.data import Dataset
import blosc2
import zarr
SCALE = np.array([1.625, 0.40625, 0.40625]) # Z, Y, X um/voxel
def load_zarr_chunk(zarr_path, t, shape, dtype):
chunk_path = os.path.join(zarr_path, '0', 'c', str(t), '0', '0', '0')
if os.path.exists(chunk_path):
try:
with open(chunk_path, 'rb') as f:
compressed = f.read()
decompressed = blosc2.decompress(compressed)
return np.frombuffer(decompressed, dtype=dtype).reshape(shape[1:]) # (Z, Y, X)
except Exception:
pass
try:
import zarr
arr = zarr.open(zarr_path, mode="r")["0"]
return np.array(arr[t], dtype=dtype)
except Exception:
return np.zeros(shape[1:], dtype=dtype)
def load_geff_nodes(geff_path):
root = zarr.open_group(geff_path, mode="r")
t_arr = root["nodes"]["props"]["t"]["values"][:]
z_arr = root["nodes"]["props"]["z"]["values"][:]
y_arr = root["nodes"]["props"]["y"]["values"][:]
x_arr = root["nodes"]["props"]["x"]["values"][:]
nodes_by_t = {}
for t, z, y, x in zip(t_arr, z_arr, y_arr, x_arr):
t = int(t)
if t not in nodes_by_t:
nodes_by_t[t] = []
nodes_by_t[t].append((int(round(z)), int(round(y)), int(round(x))))
return nodes_by_t
class BiohubCellDataset(Dataset):
def __init__(self, data_dir, sample_names, crop_size=(32, 128, 128), sigma=(1.0, 2.0, 2.0), is_train=True):
self.data_dir = data_dir
self.sample_names = sample_names
self.crop_size = crop_size
self.sigma = np.array(sigma, dtype=np.float32)
self.is_train = is_train
self.samples = []
for name in sample_names:
zarr_p = os.path.join(data_dir, f"{name}.zarr")
geff_p = os.path.join(data_dir, f"{name}.geff")
if os.path.exists(zarr_p) and os.path.exists(geff_p):
with open(os.path.join(zarr_p, '0', 'zarr.json')) as f:
meta = json.load(f)
shape = meta['shape'] # [T, Z, Y, X]
dtype = np.dtype(meta['data_type'])
nodes_by_t = load_geff_nodes(geff_p)
for t in range(shape[0]):
if t in nodes_by_t and len(nodes_by_t[t]) > 0:
self.samples.append({
'name': name,
'zarr_path': zarr_p,
't': t,
'shape': shape,
'dtype': dtype,
'nodes': nodes_by_t[t]
})
def __len__(self):
return len(self.samples)
def __getitem__(self, idx):
item = self.samples[idx]
vol = load_zarr_chunk(item['zarr_path'], item['t'], item['shape'], item['dtype']).astype(np.float32)
# Normalize volume
p1, p99 = np.percentile(vol, 1), np.percentile(vol, 99.8)
vol = np.clip((vol - p1) / max(1e-5, p99 - p1), 0.0, 1.0)
Z, Y, X = vol.shape
cz, cy, cx = self.crop_size
if self.is_train:
# Pick a center around an annotated node if possible
if len(item['nodes']) > 0 and np.random.rand() > 0.2:
node = item['nodes'][np.random.randint(len(item['nodes']))]
z_start = int(np.clip(node[0] - cz // 2, 0, Z - cz))
y_start = int(np.clip(node[1] - cy // 2, 0, Y - cy))
x_start = int(np.clip(node[2] - cx // 2, 0, X - cx))
else:
z_start = np.random.randint(0, Z - cz + 1)
y_start = np.random.randint(0, Y - cy + 1)
x_start = np.random.randint(0, X - cx + 1)
else:
z_start, y_start, x_start = (Z - cz) // 2, (Y - cy) // 2, (X - cx) // 2
vol_crop = vol[z_start:z_start+cz, y_start:y_start+cy, x_start:x_start+cx]
# Generate Gaussian heatmap target
heatmap = np.zeros((cz, cy, cx), dtype=np.float32)
sz, sy, sx = self.sigma
radius_z, radius_y, radius_x = int(3 * sz), int(3 * sy), int(3 * sx)
for (nz, ny, nx) in item['nodes']:
rz, ry, rx = nz - z_start, ny - y_start, nx - x_start
if 0 <= rz < cz and 0 <= ry < cy and 0 <= rx < cx:
z_min, z_max = max(0, rz - radius_z), min(cz, rz + radius_z + 1)
y_min, y_max = max(0, ry - radius_y), min(cy, ry + radius_y + 1)
x_min, x_max = max(0, rx - radius_x), min(cx, rx + radius_x + 1)
zz, yy, xx = np.ogrid[z_min:z_max, y_min:y_max, x_min:x_max]
dist_sq = ((zz - rz) / sz)**2 + ((yy - ry) / sy)**2 + ((xx - rx) / sx)**2
gaussian = np.exp(-0.5 * dist_sq).astype(np.float32)
heatmap[z_min:z_max, y_min:y_max, x_min:x_max] = np.maximum(
heatmap[z_min:z_max, y_min:y_max, x_min:x_max], gaussian
)
if self.is_train:
# 3D Data augmentations
if np.random.rand() > 0.5:
vol_crop = np.flip(vol_crop, axis=0)
heatmap = np.flip(heatmap, axis=0)
if np.random.rand() > 0.5:
vol_crop = np.flip(vol_crop, axis=1)
heatmap = np.flip(heatmap, axis=1)
if np.random.rand() > 0.5:
vol_crop = np.flip(vol_crop, axis=2)
heatmap = np.flip(heatmap, axis=2)
vol_tensor = torch.from_numpy(vol_crop.copy()).unsqueeze(0) # (1, Z, Y, X)
heat_tensor = torch.from_numpy(heatmap.copy()).unsqueeze(0) # (1, Z, Y, X)
return vol_tensor, heat_tensor

Xet Storage Details

Size:
5.7 kB
·
Xet hash:
388a037a71d70d3af019014e20640d55106bc2fa0b714508f9b6d2169eec1887

Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.