Add All Folders

This commit is contained in:
soorena62
2026-02-08 04:38:10 +03:30
commit 9bb57e4799
191 changed files with 8469 additions and 0 deletions
+72
View File
@@ -0,0 +1,72 @@
import json
import threading
from pathlib import Path
from loguru import logger
class WorkflowState:
def __init__(self, state_dir: Path):
self.state_dir = state_dir
self.state_file = state_dir / "state.json"
self._lock = threading.Lock()
self._state = {"steps": {}, "cancelled": False}
self.state_dir.mkdir(parents=True, exist_ok=True)
if self.state_file.exists():
self._state = json.loads(self.state_file.read_text())
def mark_success(self, step_id: str, outputs: dict):
with self._lock:
self._state["steps"][step_id] = {"status": "success", "outputs": outputs}
self._write()
def mark_failed(self, step_id: str, error: str):
with self._lock:
self._state["steps"][step_id] = {"status": "failed", "error": error}
self._write()
def get_status(self, step_id: str):
return self._state["steps"].get(step_id, {}).get("status", "pending")
def get_outputs(self, step_id: str):
return self._state["steps"].get(step_id, {}).get("outputs", {})
def set_cancelled(self, flag: bool):
with self._lock:
self._state["cancelled"] = flag
self._write()
def is_cancelled(self) -> bool:
return self._state.get("cancelled", False)
def _write(self):
self.state_file.write_text(json.dumps(self._state, indent=2))
class Node:
def __init__(self, step_id: str, run_fn, inputs: list, outputs: list):
self.step_id = step_id
self.run_fn = run_fn
self.inputs = inputs
self.outputs = outputs
class Workflow:
def __init__(self, nodes: list[Node], state: WorkflowState):
self.nodes = nodes
self.state = state
def execute(self):
for node in self.nodes:
status = self.state.get_status(node.step_id)
if status == "success":
logger.info(f"Skip {node.step_id}: already successful.")
continue
if self.state.is_cancelled():
logger.warning("Execution cancelled. Stopping workflow.")
break
try:
logger.info(f"Run {node.step_id}")
outputs = node.run_fn(self.state)
self.state.mark_success(node.step_id, outputs)
logger.info(f"Success {node.step_id}")
except Exception as e:
logger.exception(f"Failed {node.step_id}: {e}")
self.state.mark_failed(node.step_id, str(e))
break # Stop on failure, allow resume later
@@ -0,0 +1,39 @@
from dagster import op, job, In
import os, json
from app.features.pysera_node import run_pysera
DATA_DIR = os.path.join("data", "images")
MASK_DIR = os.path.join("data", "masks")
OUTPUT_DIR = os.path.join("app", "storage", "artifacts")
os.makedirs(OUTPUT_DIR, exist_ok=True)
@op(ins={"filename": In(str)})
def load_image(filename: str) -> str:
# Return normalized image file path (do not load arrays/objects)
return os.path.normpath(os.path.join(DATA_DIR, filename))
@op(ins={"filename": In(str)})
def load_mask(filename: str) -> str:
# Return normalized mask file path (do not load arrays/objects)
return os.path.normpath(os.path.join(MASK_DIR, filename))
@op
def extract_features(img_path: str, mask_path: str) -> str:
# Validate file paths and call PySERA
if not os.path.exists(img_path):
raise FileNotFoundError(f"Image path not found: {img_path}")
if not os.path.exists(mask_path):
raise FileNotFoundError(f"Mask path not found: {mask_path}")
features = run_pysera(img_path, mask_path)
out_path = os.path.join(OUTPUT_DIR, "CT_pitch_features.json")
with open(out_path, "w", encoding="utf-8") as f:
json.dump(features, f, indent=2)
return out_path
@job
def radiuma_job():
# Compose ops: produce file paths, then extract features
img = load_image()
mask = load_mask()
extract_features(img, mask)
@@ -0,0 +1,79 @@
import os
import pysera
from app.engine import radiuma_assets
from app.engine.dagster_builder import radiuma_job
from dagster import materialize, FilesystemIOManager
# Main workflow runner for Radiuma using PySERA and Dagster
def run_workflow():
# Define input image and mask paths
image_path = os.path.normpath("data/images/CT_pitch.nii.gz")
mask_path = os.path.normpath("data/masks/CT_pitch_mask.nii.gz")
output_dir = os.path.normpath("./results")
# Run PySERA radiomics workflow
# Changed dimensions from "3d" to "1st,2D"
result = pysera.process_batch(
image_input=image_path,
mask_input=mask_path,
output_path=output_dir,
categories="all",
dimensions="1st,2D", # <-- only change applied here
apply_preprocessing=True,
)
print("✅ PySERA workflow finished." if result.get("success") else "❌ PySERA workflow failed.")
def run_pipeline():
# Run Dagster job with sample configuration
result = radiuma_job.execute_in_process(
run_config={
"ops": {
"load_image": {"inputs": {"filename": {"value": "CT_pitch.nii.gz"}}},
"load_mask": {"inputs": {"filename": {"value": "CT_pitch_mask.nii.gz"}}},
}
}
)
print("✅ Dagster job finished.")
print(result)
def run_assets():
# Ensure artifacts directory exists
base_dir = os.path.normpath("app/storage/artifacts")
os.makedirs(base_dir, exist_ok=True)
# Use FilesystemIOManager for asset storage
io_manager = FilesystemIOManager(base_dir=base_dir)
# Materialize all assets defined in radiuma_assets
result = materialize(
[
radiuma_assets.raw_image,
radiuma_assets.raw_mask,
radiuma_assets.registered_image,
radiuma_assets.fused_image,
radiuma_assets.features,
],
resources={"io_manager": io_manager},
)
if result.success:
print("✅ Assets materialized successfully.")
print(f"Storage base_dir: {base_dir}")
# Print all materialized asset keys
for event in result.get_asset_materialization_events():
print(f"Asset built: {event.asset_key}")
else:
print("❌ Asset execution failed.")
print(result)
if __name__ == "__main__":
print("🚀 Starting Radiuma unified run...\n")
run_workflow()
run_pipeline()
run_assets()
print("\n🎯 All workflows completed in one run.")
@@ -0,0 +1,57 @@
import os
import SimpleITK as sitk
from dagster import asset
DATA_DIR = os.path.join("data", "images")
MASK_DIR = os.path.join("data", "masks")
ARTIFACTS_DIR = os.path.join("app", "storage", "artifacts")
os.makedirs(ARTIFACTS_DIR, exist_ok=True)
@asset
def raw_image() -> str:
# Return existing image path
path = os.path.normpath(os.path.join(DATA_DIR, "CT_pitch.nii.gz"))
if not os.path.exists(path):
raise FileNotFoundError(f"raw_image: file not found at {path}")
return path
@asset
def raw_mask() -> str:
path = os.path.normpath(os.path.join(MASK_DIR, "CT_pitch_mask.nii.gz"))
if not os.path.exists(path):
raise FileNotFoundError(f"raw_mask: file not found at {path}")
return path
@asset
def registered_image(raw_image: str) -> str:
# Read the raw image and write a registered copy
img = sitk.ReadImage(raw_image)
registered = sitk.Cast(img, img.GetPixelID()) # identity registration
out_path = os.path.normpath(os.path.join(ARTIFACTS_DIR, "CT_pitch_registered.nii.gz"))
sitk.WriteImage(registered, out_path)
return out_path
@asset
def fused_image(registered_image: str, raw_mask: str) -> str:
img = sitk.ReadImage(registered_image)
mask = sitk.ReadImage(raw_mask)
mask_uint8 = sitk.Cast(mask, sitk.sitkUInt8)
fused = sitk.Mask(img, mask_uint8)
out_path = os.path.normpath(os.path.join(ARTIFACTS_DIR, "CT_pitch_fused.nii.gz"))
sitk.WriteImage(fused, out_path)
return out_path
@asset
def features(fused_image: str) -> str:
import json, numpy as np
img = sitk.ReadImage(fused_image)
arr = sitk.GetArrayFromImage(img)
feats = {
"shape": list(arr.shape),
"spacing": list(img.GetSpacing()),
"intensity_sum": float(np.sum(arr)),
}
out_path = os.path.normpath(os.path.join(ARTIFACTS_DIR, "CT_pitch_asset_features.json"))
with open(out_path, "w", encoding="utf-8") as f:
json.dump(feats, f, indent=2)
return out_path
@@ -0,0 +1,69 @@
import json
import numpy as np
from dagster import op
import SimpleITK as sitk
from pathlib import Path
# Write Your codes Here:
ARTIFACTS = Path("artifacts")
ARTIFACTS.mkdir(exist_ok=True)
@op
def image_reader(path: str):
"""Medical image reading (MRI, CT, PET)."""
img = sitk.ReadImage(path)
# Save the processed version for display in the GUI (optional)
sitk.WriteImage(img, str(ARTIFACTS / "processed_image.nii.gz"))
return img
@op
def image_registration(fixed_img, moving_img):
"""Aligning two medical images."""
reg = sitk.ImageRegistrationMethod()
reg.SetMetricAsMeanSquares()
reg.SetOptimizerAsGradientDescent(learningRate=1.0, numberOfIterations=100)
reg.SetInterpolator(sitk.sitkLinear)
transform = reg.Execute(fixed_img, moving_img)
registered = sitk.Resample(
moving_img, fixed_img, transform, sitk.sitkLinear, 0.0, moving_img.GetPixelID()
)
sitk.WriteImage(registered, str(ARTIFACTS / "registered_image.nii.gz"))
return registered
@op
def image_fusion(img1, img2):
"""Combining two medical images (simple fusion)."""
fused = sitk.Cast((img1 + img2) / 2, sitk.sitkFloat32)
sitk.WriteImage(fused, str(ARTIFACTS / "fused_image.nii.gz"))
return fused
@op
def image_extraction(img):
"""Extracting simple features from images."""
arr = sitk.GetArrayFromImage(img)
features = {
"mean_intensity": float(np.mean(arr)),
"std_intensity": float(np.std(arr)),
"min_intensity": float(np.min(arr)),
"max_intensity": float(np.max(arr)),
}
(ARTIFACTS / "features.json").write_text(json.dumps(features, indent=2))
return features
@op
def image_writer(img):
"""Save final output."""
out_path = ARTIFACTS / "output.nii.gz"
sitk.WriteImage(img, str(out_path))
return str(out_path)
OPS = {
"reader": image_reader,
"registration": image_registration,
"fusion": image_fusion,
"extraction": image_extraction,
"writer": image_writer,
}