style: format to length 100

This commit is contained in:
2026-07-02 12:52:45 +02:00
parent 40b12c12db
commit dc6218e45f
23 changed files with 124 additions and 362 deletions
+1 -5
View File
@@ -24,11 +24,7 @@ def focus_measure_edges(
return 0.0
if verbose:
mask_sum = mask.sum() if mask is not None else roi.size
print(
f"mask pixels: {mask_sum}, "
f"focus={strong.mean():.2f}"
f"strong_size={strong.size}"
)
print(f"mask pixels: {mask_sum}, focus={strong.mean():.2f}strong_size={strong.size}")
return float(strong.mean()) # higher = sharper
+2 -6
View File
@@ -12,17 +12,13 @@ class Coordinate(BaseModel):
# Overload +
def __add__(self, other: "Coordinate") -> "Coordinate":
if isinstance(other, Coordinate):
return Coordinate(
x=self.x + other.x, y=self.y + other.y, z=self.z + other.z
)
return Coordinate(x=self.x + other.x, y=self.y + other.y, z=self.z + other.z)
return NotImplemented
# Overload -
def __sub__(self, other: "Coordinate") -> "Coordinate":
if isinstance(other, Coordinate):
return Coordinate(
x=self.x - other.x, y=self.y - other.y, z=self.z - other.z
)
return Coordinate(x=self.x - other.x, y=self.y - other.y, z=self.z - other.z)
return NotImplemented
# Overload *
+1 -3
View File
@@ -81,7 +81,5 @@ if __name__ == "__main__":
print(f"geom.max_resolution_angstrom: {geom.max_resolution_angstrom:.2f} Angstrom")
print(f"geom.calc_dtz_mm(3.9): {geom.calc_dtz_mm(3.9):.2f} mm")
print(
f"geom.resolution_angstrom(1244.42): {geom.resolution_angstrom(1244.42):.2f} Angstrom"
)
print(f"geom.resolution_angstrom(1244.42): {geom.resolution_angstrom(1244.42):.2f} Angstrom")
print(f"geom.detector_radius_mm: {geom.detector_radius_mm:.2f} mm")
+25 -75
View File
@@ -16,15 +16,11 @@ def identify_crystal_raster(result, r: RasterGridRequest) -> CenterOfMassModel |
max_image = max(images, key=lambda img: img.spots_low_res)
logger.debug(f"Image with maximum spots_low_res: {max_image}")
logger.debug(f"Maximum spots_low_res value: {max_image.spots_low_res}")
logger.debug(
f"Maximum image found at grid coordiantes {max_image.nx}, {max_image.ny}"
)
logger.debug(f"Maximum image found at grid coordiantes {max_image.nx}, {max_image.ny}")
logger.debug(
f"Maximum image found at umL {max_image.nx * r.grid_size_mm.x}, {max_image.ny * r.grid_size_mm.y}"
)
com = CenterOfMassModel(
n_x=max_image.nx, n_y=max_image.ny, max_image=max_image.number
)
com = CenterOfMassModel(n_x=max_image.nx, n_y=max_image.ny, max_image=max_image.number)
com_mm = com.get_com_mm(r)
grid_mm_x = com_mm.x
grid_mm_y = com_mm.y
@@ -168,16 +164,11 @@ def create_quality_filtered_array(
if min_spots is None:
min_spots = min(
(result.spots for result in scan_results if result.spots is not None),
default=1,
(result.spots for result in scan_results if result.spots is not None), default=1
)
if min_low_res_spots is None:
min_low_res_spots = min(
(
result.spots_low_res
for result in scan_results
if result.spots_low_res is not None
),
(result.spots_low_res for result in scan_results if result.spots_low_res is not None),
default=1,
)
if min_background is None:
@@ -187,8 +178,7 @@ def create_quality_filtered_array(
if min_efficiency is None:
min_efficiency = 1.0
max_spots_low_res = max(
(result.spots_low_res for result in scan_results if result.spots is not None),
default=1,
(result.spots_low_res for result in scan_results if result.spots is not None), default=1
)
for result in scan_results:
@@ -231,22 +221,16 @@ def get_xtal_size(crystal_size, result_array, r: RasterGridRequest):
cols = np.any(object_mask, axis=0)
row_min, row_max = np.where(rows)[0][[0, -1]]
col_min, col_max = np.where(cols)[0][[0, -1]]
logger.info(
f"Largest bbox: width={col_max - col_min}, height={row_max - row_min}"
)
logger.info(f"Largest bbox: width={col_max - col_min}, height={row_max - row_min}")
logger.info(
f"Largest bbox: width={(col_max - col_min) * r.grid_size_mm.x}, y={(row_max - row_min) * r.grid_size_mm.y}"
)
if r.n_x == 1:
logger.debug("calculating z")
crystal_size = CrystalSize(
x=crystal_size.x,
y=crystal_size.y,
z=(col_max - col_min) * r.grid_size_mm.y * 1000,
)
logger.info(
f"Crystal Size: x={crystal_size.x}, y={crystal_size.y}, z={crystal_size.z}"
x=crystal_size.x, y=crystal_size.y, z=(col_max - col_min) * r.grid_size_mm.y * 1000
)
logger.info(f"Crystal Size: x={crystal_size.x}, y={crystal_size.y}, z={crystal_size.z}")
else:
logger.debug("calculating x and y")
@@ -255,9 +239,7 @@ def get_xtal_size(crystal_size, result_array, r: RasterGridRequest):
y=(col_max - col_min) * r.grid_size_mm.y * 1000,
z=crystal_size.z,
)
logger.info(
f"Crystal Size: x={crystal_size.x}, y={crystal_size.y}, z={crystal_size.z}"
)
logger.info(f"Crystal Size: x={crystal_size.x}, y={crystal_size.y}, z={crystal_size.z}")
except ValueError as e:
logger.error(f"error calculating xtal size: {e}")
crystal_size = CrystalSize(x=0, y=0, z=0)
@@ -269,9 +251,7 @@ def get_best_b_factor(result_list: List):
if not result_list:
return None
best_b_factor = min(
(img for img in result_list if img.b is not None),
key=lambda img: img.b,
default=None,
(img for img in result_list if img.b is not None), key=lambda img: img.b, default=None
)
if best_b_factor is None:
return None
@@ -283,9 +263,7 @@ def get_best_res(result_list: List):
if not result_list:
return None
best_res = min(
(img for img in result_list if img.res is not None),
key=lambda img: img.res,
default=None,
(img for img in result_list if img.res is not None), key=lambda img: img.res, default=None
)
if best_res is None:
return None
@@ -308,9 +286,7 @@ def get_result_list_from_com(images, com: CenterOfMassModel):
start_y, end_y = round(cy - 1), round(cy + 1)
logger.info(f"range x {start_x} {end_x}, y {start_y} {end_y}")
result_list = [
img
for img in images
if start_x <= img.nx <= end_x and start_y <= img.ny <= end_y
img for img in images if start_x <= img.nx <= end_x and start_y <= img.ny <= end_y
]
return result_list
@@ -353,10 +329,7 @@ def raster_highest_score(images) -> CenterOfMassModel | None:
return _to_com_model(_max_cell(score_array), images, "Highest score")
def has_sufficient_low_res_spots(
result_array: np.ndarray,
min_spots_low_res: float,
) -> bool:
def has_sufficient_low_res_spots(result_array: np.ndarray, min_spots_low_res: float) -> bool:
if result_array is None or result_array.size == 0:
logger.warning("Empty result_array passed to has_sufficient_low_res_spots")
return False
@@ -365,20 +338,14 @@ def has_sufficient_low_res_spots(
logger.info(f"Max spots_low_res in raster result_array: {max_val}")
if max_val < min_spots_low_res:
logger.info(
"Raster rejected: max spots_low_res "
f"{max_val} < required {min_spots_low_res}"
)
logger.info(f"Raster rejected: max spots_low_res {max_val} < required {min_spots_low_res}")
return False
return True
def compute_crystal_score_array(
scan_results: List,
w_bkg: float = 0.25,
w_low_res: float = 0.55,
w_indexed: float = 0.20,
scan_results: List, w_bkg: float = 0.25, w_low_res: float = 0.55, w_indexed: float = 0.20
) -> np.ndarray:
"""Combine bkg (25%), spots_low_res (55%), and spots_indexed (20%) into a 0100 score.
@@ -395,9 +362,7 @@ def compute_crystal_score_array(
return np.zeros_like(a, dtype=float)
return (a - mn) / (mx - mn) * 100.0
return (
w_bkg * _norm(arr_bkg) + w_low_res * _norm(arr_low) + w_indexed * _norm(arr_idx)
)
return w_bkg * _norm(arr_bkg) + w_low_res * _norm(arr_low) + w_indexed * _norm(arr_idx)
def _max_cell(arr: np.ndarray) -> tuple[int, int]:
@@ -445,12 +410,7 @@ def _draw_panel(
linewidths=1.8,
)
rect = mpatches.Rectangle(
(max_nx - 0.5, max_ny - 0.5),
1,
1,
linewidth=2,
edgecolor="red",
facecolor="none",
(max_nx - 0.5, max_ny - 0.5), 1, 1, linewidth=2, edgecolor="red", facecolor="none"
)
ax.add_patch(rect)
@@ -466,10 +426,7 @@ def _draw_panel(
xtal_size = get_xtal_size(CrystalSize(x=0, y=0, z=0), mask.astype(float), r)
size_line = f"\n{xtal_size.x:.0f}×{xtal_size.y:.0f} µm"
ax.set_title(
f"{label}\nthresh≈{threshold:.1f} | {n_cells} cells{size_line}",
fontsize=8,
)
ax.set_title(f"{label}\nthresh≈{threshold:.1f} | {n_cells} cells{size_line}", fontsize=8)
ax.set_xlabel("nx", fontsize=7)
ax.set_ylabel("ny", fontsize=7)
ax.tick_params(labelsize=6)
@@ -506,9 +463,7 @@ def crystal_mask_otsu_nonzero(arr: np.ndarray) -> tuple[np.ndarray, float]:
return arr > threshold, threshold
def crystal_mask_mean_sigma(
arr: np.ndarray, n_sigma: float = 0.5
) -> tuple[np.ndarray, float]:
def crystal_mask_mean_sigma(arr: np.ndarray, n_sigma: float = 0.5) -> tuple[np.ndarray, float]:
"""Threshold = mean + n_sigma * std of nonzero values.
n_sigma=0.5 keeps cells within ~1/2 std above average signal.
@@ -569,9 +524,7 @@ def crystal_mask_dbscan(
return mask, min_signal
def crystal_mask_kmeans(
arr: np.ndarray, n_clusters: int = 3
) -> tuple[np.ndarray, float]:
def crystal_mask_kmeans(arr: np.ndarray, n_clusters: int = 3) -> tuple[np.ndarray, float]:
"""K-means on signal values (1-D feature).
Splits cells into n_clusters groups; the cluster with the highest
@@ -590,10 +543,7 @@ def crystal_mask_kmeans(
CRYSTAL_METHOD_MAP: dict = {
"corner": ("Corner background", crystal_mask_corner_background),
"otsu": ("Otsu (nonzero)", crystal_mask_otsu_nonzero),
"mean_sigma": (
"Mean + 0.5σ",
lambda arr: crystal_mask_mean_sigma(arr, n_sigma=0.5),
),
"mean_sigma": ("Mean + 0.5σ", lambda arr: crystal_mask_mean_sigma(arr, n_sigma=0.5)),
"percentile": (
"Top-40% signal",
lambda arr: crystal_mask_signal_percentile(arr, percentile=60.0),
@@ -671,9 +621,7 @@ def compare_crystal_methods_scored(
results, w_bkg=w_bkg, w_low_res=w_low_res, w_indexed=w_indexed
)
method_entries = [
(name, label, fn) for name, (label, fn) in CRYSTAL_METHOD_MAP.items()
]
method_entries = [(name, label, fn) for name, (label, fn) in CRYSTAL_METHOD_MAP.items()]
fig, axes = plt.subplots(2, 3, figsize=(15, 9))
for ax, (name, label, fn) in zip(axes.flat, method_entries):
@@ -692,7 +640,9 @@ def compare_crystal_methods_scored(
spine.set_edgecolor("yellow")
spine.set_linewidth(3)
weight_note = f"bkg×{w_bkg:.0%} + spots_low_res×{w_low_res:.0%} + spots_indexed×{w_indexed:.0%}"
weight_note = (
f"bkg×{w_bkg:.0%} + spots_low_res×{w_low_res:.0%} + spots_indexed×{w_indexed:.0%}"
)
fig.suptitle(
f"Crystal region detection on composite score [{weight_note}]\n"
"cyan = crystal outline | red square = max-score cell | score range 0100",
+4 -15
View File
@@ -69,15 +69,11 @@ class _SeedConfig:
SEED_CATALOGUE: dict[int, _SeedConfig] = {
# --- 1: single crystal, centred, compact, strong signal ----------------
1: _SeedConfig(
clusters=[
_ClusterParams(cx=0.50, cy=0.50, ax=0.15, ay=0.15, theta=0.0, peak=120),
]
clusters=[_ClusterParams(cx=0.50, cy=0.50, ax=0.15, ay=0.15, theta=0.0, peak=120)]
),
# --- 2: single crystal, off-centre, slightly elongated -----------------
2: _SeedConfig(
clusters=[
_ClusterParams(cx=0.25, cy=0.70, ax=0.12, ay=0.18, theta=0.35, peak=90),
]
clusters=[_ClusterParams(cx=0.25, cy=0.70, ax=0.12, ay=0.18, theta=0.35, peak=90)]
),
# --- 3: two well-separated crystals ------------------------------------
3: _SeedConfig(
@@ -111,11 +107,7 @@ SEED_CATALOGUE: dict[int, _SeedConfig] = {
def _gaussian_cluster(
ix: np.ndarray,
iy: np.ndarray,
p: _ClusterParams,
n_x: int,
n_y: int,
ix: np.ndarray, iy: np.ndarray, p: _ClusterParams, n_x: int, n_y: int
) -> np.ndarray:
"""Return a 2-D Gaussian signal array for one cluster.
@@ -135,10 +127,7 @@ def _gaussian_cluster(
return p.peak * np.exp(-2.0 * (dx_r**2 + dy_r**2))
def generate_no_beam_scan_result(
request: RasterGridRequest,
seed: int | None = None,
) -> ScanResult:
def generate_no_beam_scan_result(request: RasterGridRequest, seed: int | None = None) -> ScanResult:
"""Build a synthetic ScanResult for no-beam / offline operation.
Parameters