Fahimeh Orvati Nia
commited on
Commit
·
5b8812f
1
Parent(s):
916b83d
sorghum_pipeline/output/manager.py
CHANGED
|
@@ -55,7 +55,8 @@ class OutputManager:
|
|
| 55 |
try:
|
| 56 |
mask = plant_data.get('mask')
|
| 57 |
if isinstance(mask, np.ndarray):
|
| 58 |
-
|
|
|
|
| 59 |
except Exception as e:
|
| 60 |
logger.error(f"Failed to save mask: {e}")
|
| 61 |
|
|
@@ -67,7 +68,8 @@ class OutputManager:
|
|
| 67 |
overlay = self._create_overlay(base_image, mask)
|
| 68 |
# Convert BGR→RGB for correct viewing in standard image viewers
|
| 69 |
overlay_rgb = cv2.cvtColor(overlay, cv2.COLOR_BGR2RGB)
|
| 70 |
-
|
|
|
|
| 71 |
except Exception as e:
|
| 72 |
logger.error(f"Failed to save overlay: {e}")
|
| 73 |
|
|
@@ -80,7 +82,8 @@ class OutputManager:
|
|
| 80 |
base_image = self._normalize_to_uint8(base_image.astype(np.float64))
|
| 81 |
# Convert BGR→RGB for human viewing
|
| 82 |
comp_rgb = cv2.cvtColor(base_image, cv2.COLOR_BGR2RGB)
|
| 83 |
-
|
|
|
|
| 84 |
except Exception as e:
|
| 85 |
logger.error(f"Failed to save composite: {e}")
|
| 86 |
|
|
@@ -105,6 +108,7 @@ class OutputManager:
|
|
| 105 |
ax.set_axis_off()
|
| 106 |
ax.set_facecolor('white')
|
| 107 |
im = ax.imshow(masked, cmap=cmap, vmin=vmin, vmax=vmax)
|
|
|
|
| 108 |
# add colorbar
|
| 109 |
cbar = fig.colorbar(im, ax=ax, fraction=0.046, pad=0.04)
|
| 110 |
cbar.ax.tick_params(labelsize=8)
|
|
@@ -130,6 +134,7 @@ class OutputManager:
|
|
| 130 |
ax.set_axis_off()
|
| 131 |
ax.set_facecolor('white')
|
| 132 |
im = ax.imshow(img, cmap='gray', vmin=0, vmax=255)
|
|
|
|
| 133 |
cbar = fig.colorbar(im, ax=ax, fraction=0.046, pad=0.04)
|
| 134 |
cbar.ax.tick_params(labelsize=8)
|
| 135 |
plt.tight_layout()
|
|
@@ -146,7 +151,8 @@ class OutputManager:
|
|
| 146 |
images = morph.get('images', {})
|
| 147 |
size_img = images.get('size_analysis')
|
| 148 |
if isinstance(size_img, np.ndarray) and size_img.size > 0:
|
| 149 |
-
|
|
|
|
| 150 |
except Exception as e:
|
| 151 |
logger.error(f"Failed to save size analysis: {e}")
|
| 152 |
|
|
@@ -175,4 +181,44 @@ class OutputManager:
|
|
| 175 |
normalized = (arr - arr.min()) / (ptp + 1e-6) * 255
|
| 176 |
else:
|
| 177 |
normalized = np.zeros_like(arr)
|
| 178 |
-
return np.clip(normalized, 0, 255).astype(np.uint8)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 55 |
try:
|
| 56 |
mask = plant_data.get('mask')
|
| 57 |
if isinstance(mask, np.ndarray):
|
| 58 |
+
titled = self._add_title_banner(mask, 'Mask')
|
| 59 |
+
cv2.imwrite(str(results_dir / 'mask.png'), titled)
|
| 60 |
except Exception as e:
|
| 61 |
logger.error(f"Failed to save mask: {e}")
|
| 62 |
|
|
|
|
| 68 |
overlay = self._create_overlay(base_image, mask)
|
| 69 |
# Convert BGR→RGB for correct viewing in standard image viewers
|
| 70 |
overlay_rgb = cv2.cvtColor(overlay, cv2.COLOR_BGR2RGB)
|
| 71 |
+
titled = self._add_title_banner(overlay_rgb, 'Segmentation Overlay')
|
| 72 |
+
cv2.imwrite(str(results_dir / 'overlay.png'), titled)
|
| 73 |
except Exception as e:
|
| 74 |
logger.error(f"Failed to save overlay: {e}")
|
| 75 |
|
|
|
|
| 82 |
base_image = self._normalize_to_uint8(base_image.astype(np.float64))
|
| 83 |
# Convert BGR→RGB for human viewing
|
| 84 |
comp_rgb = cv2.cvtColor(base_image, cv2.COLOR_BGR2RGB)
|
| 85 |
+
titled = self._add_title_banner(comp_rgb, 'Composite (Segmentation Input)')
|
| 86 |
+
cv2.imwrite(str(results_dir / 'composite.png'), titled)
|
| 87 |
except Exception as e:
|
| 88 |
logger.error(f"Failed to save composite: {e}")
|
| 89 |
|
|
|
|
| 108 |
ax.set_axis_off()
|
| 109 |
ax.set_facecolor('white')
|
| 110 |
im = ax.imshow(masked, cmap=cmap, vmin=vmin, vmax=vmax)
|
| 111 |
+
ax.set_title(f"{name}", fontsize=12, fontweight='bold', pad=8)
|
| 112 |
# add colorbar
|
| 113 |
cbar = fig.colorbar(im, ax=ax, fraction=0.046, pad=0.04)
|
| 114 |
cbar.ax.tick_params(labelsize=8)
|
|
|
|
| 134 |
ax.set_axis_off()
|
| 135 |
ax.set_facecolor('white')
|
| 136 |
im = ax.imshow(img, cmap='gray', vmin=0, vmax=255)
|
| 137 |
+
ax.set_title('Texture: LBP (Green Band)', fontsize=12, fontweight='bold', pad=8)
|
| 138 |
cbar = fig.colorbar(im, ax=ax, fraction=0.046, pad=0.04)
|
| 139 |
cbar.ax.tick_params(labelsize=8)
|
| 140 |
plt.tight_layout()
|
|
|
|
| 151 |
images = morph.get('images', {})
|
| 152 |
size_img = images.get('size_analysis')
|
| 153 |
if isinstance(size_img, np.ndarray) and size_img.size > 0:
|
| 154 |
+
titled = self._add_title_banner(size_img, 'Morphology Size')
|
| 155 |
+
cv2.imwrite(str(results_dir / 'size.size_analysis.png'), titled)
|
| 156 |
except Exception as e:
|
| 157 |
logger.error(f"Failed to save size analysis: {e}")
|
| 158 |
|
|
|
|
| 181 |
normalized = (arr - arr.min()) / (ptp + 1e-6) * 255
|
| 182 |
else:
|
| 183 |
normalized = np.zeros_like(arr)
|
| 184 |
+
return np.clip(normalized, 0, 255).astype(np.uint8)
|
| 185 |
+
|
| 186 |
+
def _add_title_banner(self, image: np.ndarray, title: str) -> np.ndarray:
|
| 187 |
+
"""Add a top banner with centered title text to an image using OpenCV.
|
| 188 |
+
Supports grayscale or color images; returns a BGR image.
|
| 189 |
+
"""
|
| 190 |
+
if image is None or image.size == 0:
|
| 191 |
+
return image
|
| 192 |
+
|
| 193 |
+
# Ensure 3-channel BGR for drawing
|
| 194 |
+
if image.ndim == 2:
|
| 195 |
+
base_bgr = cv2.cvtColor(image, cv2.COLOR_GRAY2BGR)
|
| 196 |
+
elif image.ndim == 3 and image.shape[2] == 3:
|
| 197 |
+
base_bgr = image.copy()
|
| 198 |
+
elif image.ndim == 3 and image.shape[2] == 4:
|
| 199 |
+
base_bgr = cv2.cvtColor(image, cv2.COLOR_BGRA2BGR)
|
| 200 |
+
else:
|
| 201 |
+
# Fallback: normalize to uint8 then convert to BGR
|
| 202 |
+
norm = self._normalize_to_uint8(image.astype(np.float64))
|
| 203 |
+
base_bgr = cv2.cvtColor(norm, cv2.COLOR_GRAY2BGR)
|
| 204 |
+
|
| 205 |
+
h, w = base_bgr.shape[:2]
|
| 206 |
+
banner_height = max(30, int(0.08 * h))
|
| 207 |
+
banner = np.full((banner_height, w, 3), (245, 245, 245), dtype=np.uint8)
|
| 208 |
+
|
| 209 |
+
# Compose banner + image
|
| 210 |
+
composed = np.vstack([banner, base_bgr])
|
| 211 |
+
|
| 212 |
+
# Put centered title text
|
| 213 |
+
font = cv2.FONT_HERSHEY_SIMPLEX
|
| 214 |
+
font_scale = max(0.5, min(1.0, w / 800.0))
|
| 215 |
+
thickness = 1
|
| 216 |
+
text = str(title)
|
| 217 |
+
(tw, th), baseline = cv2.getTextSize(text, font, font_scale, thickness)
|
| 218 |
+
x = max(5, (w - tw) // 2)
|
| 219 |
+
y = (banner_height + th) // 2
|
| 220 |
+
# Slight shadow for readability
|
| 221 |
+
cv2.putText(composed, text, (x+1, y+1), font, font_scale, (0, 0, 0), thickness+1, cv2.LINE_AA)
|
| 222 |
+
cv2.putText(composed, text, (x, y), font, font_scale, (0, 80, 0), thickness+1, cv2.LINE_AA)
|
| 223 |
+
|
| 224 |
+
return composed
|