img_phy_sim.img
Image Input, Output, and Visualization Utilities
This module provides a comprehensive set of tools for loading, saving, displaying, and analyzing images, particularly for scientific and machine learning workflows. It supports grayscale and color images, normalization, inversion, block-wise statistics, and flexible visualization options for single or multiple images.
The core idea is to provide an easy interface for inspecting and comparing images, generating informative visualizations, and preparing image data for further processing.
Main features:
- Load and save images with optional normalization
- Display images with flexible size, colormap, and axis options
- Compare multiple sets of images (input, prediction, ground truth, difference)
- Advanced multi-image visualization with custom layouts and titles
- Annotate images with block-wise mean values for quick inspection
- Highlight specific rows or columns and plot their pixel profiles
- Utility functions to get image properties (bit depth, width, height)
Typical workflow:
- Load images using
open()or read multiple paths viashow_images(). - Visualize single or multiple images using
imshow()oradvanced_imshow(). - Compare predictions with ground truth using
show_samples(). - Annotate blocks or highlight pixel profiles using
plot_image_with_values()andshow_image_with_line_and_profile().
Dependencies:
- numpy
- cv2 (OpenCV)
- matplotlib
- scikit-image
Example:
img = img.open("example.png", should_scale=True)
img = img * 255 # optional scaling
img.show()
show_samples([img], [pred_img], [ground_truth], model_name="MyModel")
plot_image_with_values(img, block_size=16, cmap="gray")
line_values = show_image_with_line_and_profile([img], axis="row", index=50)
Functions:
- get_bit_depth(img) - Return bit depth of image dtype.
- get_width_height(img, channels_before=0) - Return (width, height) of an image.
- open(src, should_scale=False, should_print=True) - Load an image from disk.
- save(img, src, should_scale=False) - Save an image to disk.
- imshow(img, size=8, axis_off=True, cmap="gray") - Display an image.
- show_samples(input_samples, pred_samples, real_samples, ...) - Compare multiple images.
- advanced_imshow(img, title=None, image_width=10, ...) - Display single or batch images with customization.
- show_images(image_paths, title=None, image_width=5, ...) - Load and display images from paths.
- plot_image_with_values(img, block_size=8, ...) - Annotate image with block-wise mean values.
- show_image_with_line_and_profile(imgs, axis='row', ...) - Highlight a row/column and plot pixel values.
1""" 2**Image Input, Output, and Visualization Utilities** 3 4This module provides a comprehensive set of tools for loading, saving, displaying, 5and analyzing images, particularly for scientific and machine learning workflows. 6It supports grayscale and color images, normalization, inversion, block-wise 7statistics, and flexible visualization options for single or multiple images. 8 9The core idea is to provide an easy interface for inspecting and comparing images, 10generating informative visualizations, and preparing image data for further processing. 11 12Main features: 13- Load and save images with optional normalization 14- Display images with flexible size, colormap, and axis options 15- Compare multiple sets of images (input, prediction, ground truth, difference) 16- Advanced multi-image visualization with custom layouts and titles 17- Annotate images with block-wise mean values for quick inspection 18- Highlight specific rows or columns and plot their pixel profiles 19- Utility functions to get image properties (bit depth, width, height) 20 21Typical workflow: 221. Load images using `open()` or read multiple paths via `show_images()`. 232. Visualize single or multiple images using `imshow()` or `advanced_imshow()`. 243. Compare predictions with ground truth using `show_samples()`. 254. Annotate blocks or highlight pixel profiles using 26 `plot_image_with_values()` and `show_image_with_line_and_profile()`. 27 28Dependencies: 29- numpy 30- cv2 (OpenCV) 31- matplotlib 32- scikit-image 33 34Example: 35```python 36img = img.open("example.png", should_scale=True) 37img = img * 255 # optional scaling 38img.show() 39show_samples([img], [pred_img], [ground_truth], model_name="MyModel") 40plot_image_with_values(img, block_size=16, cmap="gray") 41line_values = show_image_with_line_and_profile([img], axis="row", index=50) 42``` 43 44Functions: 45- get_bit_depth(img) - Return bit depth of image dtype. 46- get_width_height(img, channels_before=0) - Return (width, height) of an image. 47- open(src, should_scale=False, should_print=True) - Load an image from disk. 48- save(img, src, should_scale=False) - Save an image to disk. 49- imshow(img, size=8, axis_off=True, cmap="gray") - Display an image. 50- show_samples(input_samples, pred_samples, real_samples, ...) - Compare multiple images. 51- advanced_imshow(img, title=None, image_width=10, ...) - Display single or batch images with customization. 52- show_images(image_paths, title=None, image_width=5, ...) - Load and display images from paths. 53- plot_image_with_values(img, block_size=8, ...) - Annotate image with block-wise mean values. 54- show_image_with_line_and_profile(imgs, axis='row', ...) - Highlight a row/column and plot pixel values. 55""" 56 57 58 59# --------------- 60# >>> Imports <<< 61# --------------- 62import os 63import numpy as np 64import cv2 65import matplotlib.pyplot as plt 66from skimage.measure import block_reduce # pip install scikit-image 67 68 69 70# ------------------------- 71# >>> Basic Image tools <<< 72# ------------------------- 73 74def get_bit_depth(img): 75 """ 76 Retrieve the bit depth of an image based on its NumPy data type. 77 78 Parameters: 79 img (numpy.ndarray): <br> 80 Input image array. 81 82 Returns: <br> 83 int or str:<br> 84 Bit depth of the image (8, 16, 32, or 64).<br> 85 Returns "unknown" if the data type is not recognized. 86 87 Notes: 88 The mapping is defined for common image dtypes: 89 - np.uint8 → 8-bit 90 - np.uint16 → 16-bit 91 - np.int16 → 16-bit 92 - np.float32 → 32-bit 93 - np.float64 → 64-bit 94 """ 95 dtype_to_bits = { 96 np.uint8: 8, 97 np.uint16: 16, 98 np.int16: 16, 99 np.float32: 32, 100 np.float64: 64 101 } 102 return dtype_to_bits.get(img.dtype.type, "unknown") 103 104 105def get_width_height(img, channels_before=0): 106 """ 107 Extract the width and height of an image, optionally offset by leading channels. 108 109 Parameters: 110 - img (numpy.ndarray): <br> 111 Input image array. 112 - channels_before (int, optional): <br> 113 Offset in the shape dimension if channels precede height and width (default: 0). 114 115 Returns: 116 tuple: (width, height) of the image. 117 118 Example: 119 >>> img.shape = (256, 512) 120 >>> get_width_height(img) 121 (512, 256) 122 """ 123 height, width = img.shape[0+channels_before:2+channels_before] 124 return width, height 125 126 127 128def open(src, should_scale=False, auto_scale_method=True, should_print=True, unchanged=True): 129 """ 130 Load a grayscale image from a file path. 131 132 Parameters: 133 - src (str): Path to the image file. 134 - should_scale (bool, optional):<br> 135 If True, scale pixel values to [0, 1] according to bit depth (default: False). 136 - auto_scale_method (bool, optionl): <br> 137 If True, the scaling will auto decide to up or down scale 138 if should_sclae is also True else always down scale (default: True). 139 - should_print (bool, optional): <br> 140 If True, print image info to console (default: True). 141 142 Returns: <br> 143 numpy.ndarray: Loaded grayscale image. 144 145 Example: 146 >>> img = open("example.png", should_scale=True) 147 Loaded Image: 148 - Image size: 512x256 149 - Bit depth: 8-bit 150 - Dtype: float64 151 """ 152 img = cv2.imread(src, cv2.IMREAD_UNCHANGED if unchanged else cv2.IMREAD_GRAYSCALE) 153 height, width = img.shape[:2] 154 155 if should_scale: 156 img_max = ((2**get_bit_depth(img)) -1) 157 if auto_scale_method: 158 if img.max() < img_max*0.2: # or just img.max() <= 1.1 159 img = img * img_max 160 else: 161 img = img / img_max 162 else: 163 img = img / img_max 164 165 if should_print: 166 print(f"Loaded Image:\n - Image size: {width}x{height}\ 167 \n - Bit depth: {get_bit_depth(img)}-bit\n - Dtype: {img.dtype}\ 168 \n - Mean: {img.mean()}\n - Max: {img.max()}\n - Min: {img.min()}") 169 170 return img 171 172 173 174def save(img, src, should_scale=False, auto_scale_method=True): 175 """ 176 Save an image to disk. 177 178 Parameters: 179 - img (numpy.ndarray): Image to save. 180 - src (str): <br> 181 Destination file path. 182 - should_scale (bool, optional): <br> 183 If True, scale pixel values to [0, 1] before saving (default: False). 184 - auto_scale_method (bool, optionl): <br> 185 If True, the scaling will auto decide to up or down scale 186 if should_sclae is also True else always down scale (default: True). 187 188 Notes: 189 - The function uses OpenCV's `cv2.imwrite` for saving. 190 - The scaling logic divides by the maximum value representable 191 by the bit depth, similar to the `open()` function. 192 """ 193 if should_scale: 194 img_max = ((2**get_bit_depth(img)) -1) 195 if auto_scale_method: 196 if img.max() < img_max*0.2: # or just img.max() <= 1.1 197 img = img * img_max 198 else: 199 img = img / img_max 200 else: 201 img = img / img_max 202 203 cv2.imwrite(src, img) 204 205 206 207def imshow(img, size=8, axis_off=True, cmap="gray"): 208 """ 209 Display an image using Matplotlib. 210 211 Parameters: 212 - img (numpy.ndarray): <br> 213 Image to display. 214 - size (int, optional): <br> 215 Display size in inches (default: 8). 216 - axis_off (bool, optional): <br> 217 If True, hides the axes (default: True). 218 - cmap (str, optional):<br> 219 Colormap name. Use 'random' for a random Matplotlib colormap (default: 'gray'). 220 221 Behavior: 222 - If `img` has 3 channels, it is converted from BGR to RGB. 223 - If `cmap='random'`, a random colormap is chosen and possibly reversed. 224 - Maintains the aspect ratio based on image dimensions. 225 226 Example: 227 >>> imshow(img, cmap='random') 228 # Displays the image with a randomly selected colormap. 229 """ 230 if cmap == "random": 231 cmap = np.random.choice(["viridis", 232 "magma", 233 "inferno", 234 "plasma", 235 "cividis", 236 "spring", 237 "hot", 238 "hsv", 239 "CMRmap", 240 "gnuplot", 241 "gnuplot2", 242 "jet", 243 "turbo"]) 244 cmap = cmap if np.random.random() > 0.5 else cmap+"_r" 245 246 height, width = img.shape[:2] 247 ratio = height / width 248 plt.figure(figsize=(size, round(size * ratio))) 249 if img.ndim > 2: 250 plt.imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB)) 251 else: 252 plt.imshow(img, cmap=cmap) 253 plt.axis('off' if axis_off else 'on') 254 plt.show() 255 256 257 258# ---------------------------- 259# >>> Advanced Image Tools <<< 260# ---------------------------- 261# from https://github.com/xXAI-botXx/AI/blob/main/src/helper/imshow.py 262 263def show_samples(input_samples, pred_samples, real_samples, model_name="Model", 264 n_samples=3, n_cols=4, image_width=4, cmap="gray", 265 normalize=True, invert=False, axis=False, 266 save_to=None, hspace=0.3, wspace=0.2, use_original_style=False): 267 """ 268 Display multiple sets of sample images (input, prediction, ground truth, difference) 269 side by side for visual comparison. 270 271 The function can load images from file paths or accept NumPy arrays directly. 272 It arranges them in a grid and can optionally normalize, invert, or save the output. 273 274 Parameters: 275 - input_samples (list[str] or list[np.ndarray]): <br> 276 Input sample images. 277 - pred_samples (list[str] or list[np.ndarray]): <br> 278 Model prediction images. 279 - real_samples (list[str] or list[np.ndarray]): <br> 280 Ground truth images. 281 - model_name (str, optional): <br> 282 Name of the model to display in titles (default: "Model"). 283 - n_samples (int, optional): <br> 284 Number of sample groups to display (default: 3). 285 - n_cols (int, optional): <br> 286 Number of columns per sample group (default: 4).<br> 287 Typically: Input | Prediction | Ground Truth | Difference. 288 - image_width (int, optional): <br> 289 Width of one image in inches (default: 4). 290 - cmap (str, optional): <br> 291 Colormap for displaying grayscale images (default: "gray"). 292 - normalize (bool, optional): <br> 293 Whether to normalize pixel values to [0, 1] (default: True). 294 - invert (bool, optional): <br> 295 Whether to invert pixel values (255 - img) (default: False). 296 - axis (bool, optional):<br> 297 Whether to show image axes (default: False). 298 - save_to (str, optional): <br> 299 Path to save the figure (default: None). 300 - hspace (float, optional): <br> 301 Vertical spacing between subplots (default: 0.3). 302 - wspace (float, optional): <br> 303 Horizontal spacing between subplots (default: 0.2). 304 - use_original_style (bool, optional): <br> 305 If True, preserves the current matplotlib style (default: False). 306 307 Returns:<br> 308 None 309 310 Example: 311 >>> show_samples(inputs, preds, reals, model_name="UNet", n_samples=5, cmap="gray") 312 """ 313 314 def load_image(img): 315 if isinstance(img, str): 316 arr = cv2.imread(img, cv2.IMREAD_GRAYSCALE).astype(np.float32) 317 else: 318 arr = img.astype(np.float32) 319 if invert: 320 arr = 255 - arr 321 if normalize: 322 arr /= 255.0 323 return arr 324 325 # Prepare images and titles 326 all_images = [] 327 titles = [] 328 sub_images = [] 329 330 for idx in range(n_samples): 331 all_images.extend([input_samples[idx], pred_samples[idx], real_samples[idx], pred_samples[idx]]) 332 titles.extend(["Input", model_name, "Ground Truth", "Difference"]) 333 sub_images.extend([None, None, None, real_samples[idx]]) 334 335 # Load all images 336 img_arrays = [load_image(im) for im in all_images] 337 sub_arrays = [load_image(im) if im is not None else None for im in sub_images] 338 339 # Compute differences where applicable 340 for i, sub in enumerate(sub_arrays): 341 if sub is not None: 342 img_arrays[i] = np.abs(img_arrays[i] - sub) 343 344 n_images = len(img_arrays) 345 n_rows = n_images // n_cols + int(n_images % n_cols > 0) 346 347 fig, axes = plt.subplots(n_rows, n_cols, figsize=(n_cols*image_width, n_rows*image_width)) 348 349 if not use_original_style: 350 plt_style = 'seaborn-v0_8' if 'seaborn-v0_8' in plt.style.available else 'classic' 351 plt.style.use(plt_style) 352 353 axes = axes.ravel() if n_images > 1 else [axes] 354 355 for idx, ax in enumerate(axes): 356 if idx >= n_images: 357 ax.axis('off') 358 continue 359 ax.imshow(img_arrays[idx], cmap=cmap) 360 ax.set_title(titles[idx], fontsize=10) 361 if not axis: 362 ax.axis('off') 363 364 plt.subplots_adjust(hspace=hspace, wspace=wspace) 365 366 if save_to: 367 os.makedirs(os.path.dirname(save_to), exist_ok=True) 368 plt.savefig(save_to, dpi=300) 369 370 plt.show() 371 if not use_original_style: 372 plt.style.use('default') 373 374 375 376def advanced_imshow(img, title=None, image_width=10, axis=False, 377 color_space="RGB", cmap=None, cols=1, save_to=None, 378 hspace=0.2, wspace=0.2, 379 use_original_style=False, invert=False): 380 """ 381 Display one or multiple images in a flexible and configurable grid. 382 383 This function supports multiple color spaces, automatic reshaping of 384 input tensors, batch display, color inversion, and saving to disk. 385 386 Parameters: 387 - img (np.ndarray): <br> 388 Input image or batch of images.<br> 389 Accepted shapes:<br> 390 [H, W], [H, W, C], [N, H, W], or [N, H, W, C]. 391 - title (str or list[str], optional): <br> 392 Overall or per-image titles. 393 - image_width (int, optional): <br> 394 Width of each image in inches (default: 10). 395 - axis (bool, optional): <br> 396 Whether to show axes (default: False). 397 - color_space (str, optional): <br> 398 Color space of the image: "RGB", "BGR", "gray", or "HSV" (default: "RGB"). 399 - cmap (str, optional): <br> 400 Matplotlib colormap for grayscale images (default: None). 401 - cols (int, optional): <br> 402 Number of columns in the subplot grid (default: 1). 403 - save_to (str, optional): <br> 404 File path to save the figure (default: None). 405 - hspace (float, optional): <br> 406 Vertical spacing between subplots (default: 0.2). 407 - wspace (float, optional): <br> 408 Horizontal spacing between subplots (default: 0.2). 409 - use_original_style (bool, optional): <br> 410 Keep current Matplotlib style if True (default: False). 411 - invert (bool, optional): <br> 412 Invert color values (default: False). 413 414 Returns:<br> 415 None 416 417 Example: 418 >>> advanced_imshow(batch_images, cols=3, color_space="BGR", title="Predictions") 419 """ 420 original_style = plt.rcParams.copy() 421 try: 422 img_shape = img.shape 423 except Exception: 424 img = np.array(img) 425 img_shape = img.shape 426 # Transform to 4D array [N, H, W, C] 427 if len(img_shape) == 2: 428 img = img.reshape(1, img_shape[0], img_shape[1], 1) 429 elif len(img_shape) == 3: 430 if img_shape[2] in [1, 3]: # single image with channels 431 img = img.reshape(1, img_shape[0], img_shape[1], img_shape[2]) 432 else: # multiple gray images [N,H,W] 433 img = img[..., np.newaxis] 434 elif len(img_shape) != 4: 435 raise ValueError(f"Image(s) have wrong shape: {img_shape}") 436 437 n_images = img.shape[0] 438 aspect_ratio = img.shape[2] / img.shape[1] 439 rows = n_images // cols + int(n_images % cols > 0) 440 width = int(image_width * cols) 441 height = int(image_width * rows * aspect_ratio) 442 443 if not use_original_style: 444 plt_style = 'seaborn-v0_8' if 'seaborn-v0_8' in plt.style.available else 'classic' 445 plt.style.use(plt_style) 446 447 fig, axes = plt.subplots(nrows=rows, ncols=cols, figsize=(width, height)) 448 axes = np.array(axes).ravel() 449 fig.subplots_adjust(hspace=hspace, wspace=wspace) 450 451 if isinstance(title, str): 452 fig.suptitle(title, fontsize=20, y=0.95) 453 454 # Invert images if needed 455 if invert: 456 max_val = 2**(img.dtype.itemsize*8) - 1 457 img = max_val - img 458 459 for idx, ax in enumerate(axes): 460 if idx >= n_images: 461 ax.axis("off") 462 continue 463 cur_img = img[idx] 464 465 # Handle color spaces 466 used_cmap = cmap 467 if color_space.lower() == "bgr" and cur_img.shape[2] == 3: 468 cur_img = cv2.cvtColor(cur_img, cv2.COLOR_BGR2RGB) 469 used_cmap = None 470 elif color_space.lower() == "hsv" and cur_img.shape[2] == 3: 471 cur_img = cv2.cvtColor(cur_img, cv2.COLOR_HSV2RGB) 472 used_cmap = None 473 elif color_space.lower() in ["gray", "grey", "g"]: 474 if cur_img.shape[2] == 3: 475 cur_img = cv2.cvtColor(cur_img, cv2.COLOR_RGB2GRAY) 476 used_cmap = "gray" 477 478 if isinstance(title, (list, tuple)): 479 ax.set_title(title[idx], fontsize=12) 480 if not axis: 481 ax.axis("off") 482 483 ax.imshow(cur_img.squeeze(), cmap=used_cmap) 484 485 if save_to: 486 os.makedirs(os.path.dirname(save_to), exist_ok=True) 487 fig.savefig(save_to, dpi=300) 488 489 plt.show() 490 if not use_original_style: 491 plt.rcParams.update(original_style) 492 493 494 495def show_images(image_paths: list, title=None, image_width=5, axis=False, 496 color_space="gray", cmap=None, 497 cols=2, save_to=None, hspace=0.01, wspace=0.01, 498 use_original_style=False, invert=False): 499 """ 500 Load and display multiple images from disk using `advanced_imshow`. 501 502 Parameters: 503 - image_paths (list[str]): <br> 504 List of file paths to load. 505 - title (str or list[str], optional): <br> 506 Plot title(s). 507 - image_width (int, optional): <br> 508 Width of each image (default: 5). 509 - axis (bool, optional): <br> 510 Whether to display axes (default: False). 511 - color_space (str, optional): <br> 512 Color space to convert images to.<br> 513 One of: "gray", "rgb", "hsv", "bgr" (default: "gray"). 514 - cmap (str, optional): <br> 515 Colormap for grayscale images (default: None). 516 - cols (int, optional): <br> 517 Number of columns in the grid (default: 2). 518 - save_to (str, optional): <br> 519 Path to save the figure (default: None). 520 - hspace (float, optional): <br> 521 Vertical spacing between subplots (default: 0.01). 522 - wspace (float, optional): <br> 523 Horizontal spacing between subplots (default: 0.01). 524 - use_original_style (bool, optional): <br> 525 Keep current Matplotlib style (default: False). 526 - invert (bool, optional): <br> 527 Whether to invert images (default: False). 528 529 Returns: 530 np.ndarray: Loaded images stacked as an array. 531 532 Example: 533 >>> show_images(["img1.png", "img2.png"], color_space="rgb", cols=2) 534 """ 535 images = [] 536 for img_path in image_paths: 537 img = cv2.imread(img_path) 538 if color_space.lower() == "rgb": 539 img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) 540 elif color_space.lower() == "hsv": 541 img = cv2.cvtColor(img, cv2.COLOR_BGR2HSV) 542 elif color_space.lower() in ["gray", "grey", "g"]: 543 img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) 544 images.append(img) 545 546 images = np.array(images) 547 advanced_imshow(images, title=title, image_width=image_width, axis=axis, 548 color_space=color_space, cmap=cmap, cols=cols, save_to=save_to, 549 hspace=hspace, wspace=wspace, 550 use_original_style=use_original_style, invert=invert) 551 return images 552 553 554 555def plot_image_with_values(img, block_size=8, cmap='gray', title=None, 556 font_size=6, save_to=None): 557 """ 558 Plot an image with annotated mean values over non-overlapping blocks. 559 560 Each block represents the mean pixel intensity of its region. The mean 561 values are displayed as text annotations directly on the image. 562 563 Parameters: 564 - img (np.ndarray): <br> 565 2D grayscale image (H, W) or 3D single-channel image (H, W, 1). 566 - block_size (int or tuple, optional): <br> 567 Size of each block (default: 8). 568 - cmap (str, optional): <br> 569 Matplotlib colormap (default: "gray"). 570 - title (str, optional): <br> 571 Plot title (default: None). 572 - font_size (int, optional): <br> 573 Font size of value annotations (default: 6). 574 - save_to (str, optional): <br> 575 Path to save the figure (default: None). 576 577 Returns:<br> 578 None 579 580 Example: 581 ```python 582 from imshow import plot_image_with_values 583 import cv2 584 585 img = cv2.imread('example.png', cv2.IMREAD_GRAYSCALE) 586 plot_image_with_values(img, block_size=16, cmap='gray', title='Mean Block Values', font_size=8) 587 ``` 588 589 Or: 590 ```python 591 fig, ax = plt.subplots(nrows=2, ncols=4, figsize=(4*4, 6)) 592 idx = 0 593 594 img_1 = plot(ax[0][0], path=input_samples[idx], title=f"Input", cmap="gray") 595 plot_image_with_values(img_1, block_size=16, ax=ax[1][0]) 596 597 img_2 = plot(ax[0][1], path=pred_model[idx], title=f"{model_name}") 598 plot_image_with_values(img_2, block_size=16, ax=ax[1][1]) 599 600 img_3 = plot(ax[0][2], path=real[idx], title=f"ground truth") 601 plot_image_with_values(img_3, block_size=16, ax=ax[1][2]) 602 603 img_4 = plot(ax[0][3], path=pred_model[idx], title=f"Difference", sub_image=real[idx]) 604 plot_image_with_values(img_4, block_size=16, ax=ax[1][3]) 605 606 plt.show() 607 ``` 608 """ 609 # Ensure 2D image 610 if img.ndim == 3 and img.shape[2] == 1: 611 img = img[:, :, 0] 612 elif img.ndim == 3 and img.shape[2] > 1: 613 raise ValueError("Only grayscale images are supported for block annotation.") 614 615 # Compute mean over blocks 616 mean_img = block_reduce(img, block_size=(block_size, block_size), func=np.mean) 617 max_value = mean_img.max() 618 619 # Plot 620 plt.figure(figsize=(mean_img.shape[1]/2, mean_img.shape[0]/2)) 621 plt.imshow(mean_img, cmap=cmap, interpolation='nearest') 622 plt.colorbar(label='Mean Value') 623 624 # Annotate each block 625 for i in range(mean_img.shape[0]): 626 for j in range(mean_img.shape[1]): 627 val = mean_img[i, j] 628 color = 'white' if val < max_value/1.5 else 'black' 629 plt.text(j, i, f'{val:.1f}', ha='center', va='center', color=color, fontsize=font_size) 630 631 plt.title(title or f'Mean Values over {block_size}x{block_size} Blocks') 632 plt.xticks([]) 633 plt.yticks([]) 634 plt.tight_layout() 635 636 if save_to: 637 os.makedirs(os.path.dirname(save_to), exist_ok=True) 638 plt.savefig(save_to, dpi=300) 639 640 plt.show() 641 642 643def show_image_with_line_and_profile(imgs, axis='row', index=None, titles=None, figsize=(10, 4)): 644 """ 645 Display one or multiple grayscale images with a highlighted line (row or column) 646 and plot the corresponding pixel intensity profile below or beside each image. 647 648 Parameters: 649 - imgs (list[np.ndarray]):<br> 650 List of grayscale images to analyze. 651 - axis (str, optional):<br> 652 Direction of the line ("row" or "column") (default: "row"). 653 - index (int, optional): <br> 654 Index of the selected line. If None, the central line is used (default: None). 655 - titles (list[str], optional): <br> 656 Titles for each image (default: ["Image 1", "Image 2", ...]). 657 - figsize (tuple, optional): <br> 658 Figure size per image pair (default: (10, 4)). 659 660 Returns: 661 - list[np.ndarray]: <br> 662 List of pixel intensity profiles corresponding to the selected line in each image. 663 664 Example: 665 >>> show_image_with_line_and_profile( 666 ... imgs=[img_input, img_pred, img_gt], 667 ... axis="row", 668 ... titles=["Input", "Prediction", "Ground Truth"] 669 ... ) 670 """ 671 assert axis in ['row', 'column'], "axis must be 'row' or 'column'" 672 673 n_images = len(imgs) 674 if titles is None: 675 titles = [f"Image {i+1}" for i in range(n_images)] 676 line_values_list = [] 677 678 fig, axes = plt.subplots(n_images, 2, figsize=(figsize[0]*2, figsize[1]*n_images)) 679 680 # Ensure axes is 2D 681 if n_images == 1: 682 axes = np.expand_dims(axes, axis=0) 683 684 for i, (img, ax_pair, title) in enumerate(zip(imgs, axes, titles)): 685 h, w = img.shape 686 if index is None: 687 idx = h // 2 if axis == 'row' else w // 2 688 else: 689 idx = index 690 691 ax_img, ax_profile = ax_pair 692 693 # Show image with line 694 ax_img.imshow(img, cmap='gray', interpolation='nearest') 695 if axis == 'row': 696 ax_img.axhline(idx, color='red', linewidth=1) 697 line_values = img[idx, :] 698 else: 699 ax_img.axvline(idx, color='red', linewidth=1) 700 line_values = img[:, idx] 701 line_values_list.append(line_values) 702 703 ax_img.set_title(title) 704 ax_img.set_xticks([]) 705 ax_img.set_yticks([]) 706 707 # Plot pixel values along the line 708 x = np.arange(len(line_values)) 709 ax_profile.plot(x, line_values, color='black', linewidth=1) 710 ax_profile.set_title(f'Pixel values along {axis} {idx}') 711 ax_profile.set_xlabel('Pixel index') 712 ax_profile.set_ylabel('Intensity') 713 ax_profile.grid(True) 714 715 plt.tight_layout() 716 plt.show() 717 718 return line_values_list
75def get_bit_depth(img): 76 """ 77 Retrieve the bit depth of an image based on its NumPy data type. 78 79 Parameters: 80 img (numpy.ndarray): <br> 81 Input image array. 82 83 Returns: <br> 84 int or str:<br> 85 Bit depth of the image (8, 16, 32, or 64).<br> 86 Returns "unknown" if the data type is not recognized. 87 88 Notes: 89 The mapping is defined for common image dtypes: 90 - np.uint8 → 8-bit 91 - np.uint16 → 16-bit 92 - np.int16 → 16-bit 93 - np.float32 → 32-bit 94 - np.float64 → 64-bit 95 """ 96 dtype_to_bits = { 97 np.uint8: 8, 98 np.uint16: 16, 99 np.int16: 16, 100 np.float32: 32, 101 np.float64: 64 102 } 103 return dtype_to_bits.get(img.dtype.type, "unknown")
Retrieve the bit depth of an image based on its NumPy data type.
Parameters:
img (numpy.ndarray):
Input image array.
Returns:
int or str:
Bit depth of the image (8, 16, 32, or 64).
Returns "unknown" if the data type is not recognized.
Notes: The mapping is defined for common image dtypes: - np.uint8 → 8-bit - np.uint16 → 16-bit - np.int16 → 16-bit - np.float32 → 32-bit - np.float64 → 64-bit
106def get_width_height(img, channels_before=0): 107 """ 108 Extract the width and height of an image, optionally offset by leading channels. 109 110 Parameters: 111 - img (numpy.ndarray): <br> 112 Input image array. 113 - channels_before (int, optional): <br> 114 Offset in the shape dimension if channels precede height and width (default: 0). 115 116 Returns: 117 tuple: (width, height) of the image. 118 119 Example: 120 >>> img.shape = (256, 512) 121 >>> get_width_height(img) 122 (512, 256) 123 """ 124 height, width = img.shape[0+channels_before:2+channels_before] 125 return width, height
Extract the width and height of an image, optionally offset by leading channels.
Parameters:
- img (numpy.ndarray):
Input image array.
- channels_before (int, optional):
Offset in the shape dimension if channels precede height and width (default: 0).
Returns: tuple: (width, height) of the image.
Example:
img.shape = (256, 512) get_width_height(img) (512, 256)
129def open(src, should_scale=False, auto_scale_method=True, should_print=True, unchanged=True): 130 """ 131 Load a grayscale image from a file path. 132 133 Parameters: 134 - src (str): Path to the image file. 135 - should_scale (bool, optional):<br> 136 If True, scale pixel values to [0, 1] according to bit depth (default: False). 137 - auto_scale_method (bool, optionl): <br> 138 If True, the scaling will auto decide to up or down scale 139 if should_sclae is also True else always down scale (default: True). 140 - should_print (bool, optional): <br> 141 If True, print image info to console (default: True). 142 143 Returns: <br> 144 numpy.ndarray: Loaded grayscale image. 145 146 Example: 147 >>> img = open("example.png", should_scale=True) 148 Loaded Image: 149 - Image size: 512x256 150 - Bit depth: 8-bit 151 - Dtype: float64 152 """ 153 img = cv2.imread(src, cv2.IMREAD_UNCHANGED if unchanged else cv2.IMREAD_GRAYSCALE) 154 height, width = img.shape[:2] 155 156 if should_scale: 157 img_max = ((2**get_bit_depth(img)) -1) 158 if auto_scale_method: 159 if img.max() < img_max*0.2: # or just img.max() <= 1.1 160 img = img * img_max 161 else: 162 img = img / img_max 163 else: 164 img = img / img_max 165 166 if should_print: 167 print(f"Loaded Image:\n - Image size: {width}x{height}\ 168 \n - Bit depth: {get_bit_depth(img)}-bit\n - Dtype: {img.dtype}\ 169 \n - Mean: {img.mean()}\n - Max: {img.max()}\n - Min: {img.min()}") 170 171 return img
Load a grayscale image from a file path.
Parameters:
- src (str): Path to the image file.
- should_scale (bool, optional):
If True, scale pixel values to [0, 1] according to bit depth (default: False).
- auto_scale_method (bool, optionl):
If True, the scaling will auto decide to up or down scale
if should_sclae is also True else always down scale (default: True).
- should_print (bool, optional):
If True, print image info to console (default: True).
Returns:
numpy.ndarray: Loaded grayscale image.
Example:
img = open("example.png", should_scale=True) Loaded Image: - Image size: 512x256 - Bit depth: 8-bit - Dtype: float64
175def save(img, src, should_scale=False, auto_scale_method=True): 176 """ 177 Save an image to disk. 178 179 Parameters: 180 - img (numpy.ndarray): Image to save. 181 - src (str): <br> 182 Destination file path. 183 - should_scale (bool, optional): <br> 184 If True, scale pixel values to [0, 1] before saving (default: False). 185 - auto_scale_method (bool, optionl): <br> 186 If True, the scaling will auto decide to up or down scale 187 if should_sclae is also True else always down scale (default: True). 188 189 Notes: 190 - The function uses OpenCV's `cv2.imwrite` for saving. 191 - The scaling logic divides by the maximum value representable 192 by the bit depth, similar to the `open()` function. 193 """ 194 if should_scale: 195 img_max = ((2**get_bit_depth(img)) -1) 196 if auto_scale_method: 197 if img.max() < img_max*0.2: # or just img.max() <= 1.1 198 img = img * img_max 199 else: 200 img = img / img_max 201 else: 202 img = img / img_max 203 204 cv2.imwrite(src, img)
Save an image to disk.
Parameters:
- img (numpy.ndarray): Image to save.
- src (str):
Destination file path.
- should_scale (bool, optional):
If True, scale pixel values to [0, 1] before saving (default: False).
- auto_scale_method (bool, optionl):
If True, the scaling will auto decide to up or down scale
if should_sclae is also True else always down scale (default: True).
Notes:
- The function uses OpenCV's cv2.imwrite for saving.
- The scaling logic divides by the maximum value representable
by the bit depth, similar to the open() function.
208def imshow(img, size=8, axis_off=True, cmap="gray"): 209 """ 210 Display an image using Matplotlib. 211 212 Parameters: 213 - img (numpy.ndarray): <br> 214 Image to display. 215 - size (int, optional): <br> 216 Display size in inches (default: 8). 217 - axis_off (bool, optional): <br> 218 If True, hides the axes (default: True). 219 - cmap (str, optional):<br> 220 Colormap name. Use 'random' for a random Matplotlib colormap (default: 'gray'). 221 222 Behavior: 223 - If `img` has 3 channels, it is converted from BGR to RGB. 224 - If `cmap='random'`, a random colormap is chosen and possibly reversed. 225 - Maintains the aspect ratio based on image dimensions. 226 227 Example: 228 >>> imshow(img, cmap='random') 229 # Displays the image with a randomly selected colormap. 230 """ 231 if cmap == "random": 232 cmap = np.random.choice(["viridis", 233 "magma", 234 "inferno", 235 "plasma", 236 "cividis", 237 "spring", 238 "hot", 239 "hsv", 240 "CMRmap", 241 "gnuplot", 242 "gnuplot2", 243 "jet", 244 "turbo"]) 245 cmap = cmap if np.random.random() > 0.5 else cmap+"_r" 246 247 height, width = img.shape[:2] 248 ratio = height / width 249 plt.figure(figsize=(size, round(size * ratio))) 250 if img.ndim > 2: 251 plt.imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB)) 252 else: 253 plt.imshow(img, cmap=cmap) 254 plt.axis('off' if axis_off else 'on') 255 plt.show()
Display an image using Matplotlib.
Parameters:
- img (numpy.ndarray):
Image to display.
- size (int, optional):
Display size in inches (default: 8).
- axis_off (bool, optional):
If True, hides the axes (default: True).
- cmap (str, optional):
Colormap name. Use 'random' for a random Matplotlib colormap (default: 'gray').
Behavior:
- If img has 3 channels, it is converted from BGR to RGB.
- If cmap='random', a random colormap is chosen and possibly reversed.
- Maintains the aspect ratio based on image dimensions.
Example:
imshow(img, cmap='random') # Displays the image with a randomly selected colormap.
264def show_samples(input_samples, pred_samples, real_samples, model_name="Model", 265 n_samples=3, n_cols=4, image_width=4, cmap="gray", 266 normalize=True, invert=False, axis=False, 267 save_to=None, hspace=0.3, wspace=0.2, use_original_style=False): 268 """ 269 Display multiple sets of sample images (input, prediction, ground truth, difference) 270 side by side for visual comparison. 271 272 The function can load images from file paths or accept NumPy arrays directly. 273 It arranges them in a grid and can optionally normalize, invert, or save the output. 274 275 Parameters: 276 - input_samples (list[str] or list[np.ndarray]): <br> 277 Input sample images. 278 - pred_samples (list[str] or list[np.ndarray]): <br> 279 Model prediction images. 280 - real_samples (list[str] or list[np.ndarray]): <br> 281 Ground truth images. 282 - model_name (str, optional): <br> 283 Name of the model to display in titles (default: "Model"). 284 - n_samples (int, optional): <br> 285 Number of sample groups to display (default: 3). 286 - n_cols (int, optional): <br> 287 Number of columns per sample group (default: 4).<br> 288 Typically: Input | Prediction | Ground Truth | Difference. 289 - image_width (int, optional): <br> 290 Width of one image in inches (default: 4). 291 - cmap (str, optional): <br> 292 Colormap for displaying grayscale images (default: "gray"). 293 - normalize (bool, optional): <br> 294 Whether to normalize pixel values to [0, 1] (default: True). 295 - invert (bool, optional): <br> 296 Whether to invert pixel values (255 - img) (default: False). 297 - axis (bool, optional):<br> 298 Whether to show image axes (default: False). 299 - save_to (str, optional): <br> 300 Path to save the figure (default: None). 301 - hspace (float, optional): <br> 302 Vertical spacing between subplots (default: 0.3). 303 - wspace (float, optional): <br> 304 Horizontal spacing between subplots (default: 0.2). 305 - use_original_style (bool, optional): <br> 306 If True, preserves the current matplotlib style (default: False). 307 308 Returns:<br> 309 None 310 311 Example: 312 >>> show_samples(inputs, preds, reals, model_name="UNet", n_samples=5, cmap="gray") 313 """ 314 315 def load_image(img): 316 if isinstance(img, str): 317 arr = cv2.imread(img, cv2.IMREAD_GRAYSCALE).astype(np.float32) 318 else: 319 arr = img.astype(np.float32) 320 if invert: 321 arr = 255 - arr 322 if normalize: 323 arr /= 255.0 324 return arr 325 326 # Prepare images and titles 327 all_images = [] 328 titles = [] 329 sub_images = [] 330 331 for idx in range(n_samples): 332 all_images.extend([input_samples[idx], pred_samples[idx], real_samples[idx], pred_samples[idx]]) 333 titles.extend(["Input", model_name, "Ground Truth", "Difference"]) 334 sub_images.extend([None, None, None, real_samples[idx]]) 335 336 # Load all images 337 img_arrays = [load_image(im) for im in all_images] 338 sub_arrays = [load_image(im) if im is not None else None for im in sub_images] 339 340 # Compute differences where applicable 341 for i, sub in enumerate(sub_arrays): 342 if sub is not None: 343 img_arrays[i] = np.abs(img_arrays[i] - sub) 344 345 n_images = len(img_arrays) 346 n_rows = n_images // n_cols + int(n_images % n_cols > 0) 347 348 fig, axes = plt.subplots(n_rows, n_cols, figsize=(n_cols*image_width, n_rows*image_width)) 349 350 if not use_original_style: 351 plt_style = 'seaborn-v0_8' if 'seaborn-v0_8' in plt.style.available else 'classic' 352 plt.style.use(plt_style) 353 354 axes = axes.ravel() if n_images > 1 else [axes] 355 356 for idx, ax in enumerate(axes): 357 if idx >= n_images: 358 ax.axis('off') 359 continue 360 ax.imshow(img_arrays[idx], cmap=cmap) 361 ax.set_title(titles[idx], fontsize=10) 362 if not axis: 363 ax.axis('off') 364 365 plt.subplots_adjust(hspace=hspace, wspace=wspace) 366 367 if save_to: 368 os.makedirs(os.path.dirname(save_to), exist_ok=True) 369 plt.savefig(save_to, dpi=300) 370 371 plt.show() 372 if not use_original_style: 373 plt.style.use('default')
Display multiple sets of sample images (input, prediction, ground truth, difference) side by side for visual comparison.
The function can load images from file paths or accept NumPy arrays directly. It arranges them in a grid and can optionally normalize, invert, or save the output.
Parameters:
- input_samples (list[str] or list[np.ndarray]):
Input sample images.
- pred_samples (list[str] or list[np.ndarray]):
Model prediction images.
- real_samples (list[str] or list[np.ndarray]):
Ground truth images.
- model_name (str, optional):
Name of the model to display in titles (default: "Model").
- n_samples (int, optional):
Number of sample groups to display (default: 3).
- n_cols (int, optional):
Number of columns per sample group (default: 4).
Typically: Input | Prediction | Ground Truth | Difference.
- image_width (int, optional):
Width of one image in inches (default: 4).
- cmap (str, optional):
Colormap for displaying grayscale images (default: "gray").
- normalize (bool, optional):
Whether to normalize pixel values to [0, 1] (default: True).
- invert (bool, optional):
Whether to invert pixel values (255 - img) (default: False).
- axis (bool, optional):
Whether to show image axes (default: False).
- save_to (str, optional):
Path to save the figure (default: None).
- hspace (float, optional):
Vertical spacing between subplots (default: 0.3).
- wspace (float, optional):
Horizontal spacing between subplots (default: 0.2).
- use_original_style (bool, optional):
If True, preserves the current matplotlib style (default: False).
Returns:
None
Example:
show_samples(inputs, preds, reals, model_name="UNet", n_samples=5, cmap="gray")
377def advanced_imshow(img, title=None, image_width=10, axis=False, 378 color_space="RGB", cmap=None, cols=1, save_to=None, 379 hspace=0.2, wspace=0.2, 380 use_original_style=False, invert=False): 381 """ 382 Display one or multiple images in a flexible and configurable grid. 383 384 This function supports multiple color spaces, automatic reshaping of 385 input tensors, batch display, color inversion, and saving to disk. 386 387 Parameters: 388 - img (np.ndarray): <br> 389 Input image or batch of images.<br> 390 Accepted shapes:<br> 391 [H, W], [H, W, C], [N, H, W], or [N, H, W, C]. 392 - title (str or list[str], optional): <br> 393 Overall or per-image titles. 394 - image_width (int, optional): <br> 395 Width of each image in inches (default: 10). 396 - axis (bool, optional): <br> 397 Whether to show axes (default: False). 398 - color_space (str, optional): <br> 399 Color space of the image: "RGB", "BGR", "gray", or "HSV" (default: "RGB"). 400 - cmap (str, optional): <br> 401 Matplotlib colormap for grayscale images (default: None). 402 - cols (int, optional): <br> 403 Number of columns in the subplot grid (default: 1). 404 - save_to (str, optional): <br> 405 File path to save the figure (default: None). 406 - hspace (float, optional): <br> 407 Vertical spacing between subplots (default: 0.2). 408 - wspace (float, optional): <br> 409 Horizontal spacing between subplots (default: 0.2). 410 - use_original_style (bool, optional): <br> 411 Keep current Matplotlib style if True (default: False). 412 - invert (bool, optional): <br> 413 Invert color values (default: False). 414 415 Returns:<br> 416 None 417 418 Example: 419 >>> advanced_imshow(batch_images, cols=3, color_space="BGR", title="Predictions") 420 """ 421 original_style = plt.rcParams.copy() 422 try: 423 img_shape = img.shape 424 except Exception: 425 img = np.array(img) 426 img_shape = img.shape 427 # Transform to 4D array [N, H, W, C] 428 if len(img_shape) == 2: 429 img = img.reshape(1, img_shape[0], img_shape[1], 1) 430 elif len(img_shape) == 3: 431 if img_shape[2] in [1, 3]: # single image with channels 432 img = img.reshape(1, img_shape[0], img_shape[1], img_shape[2]) 433 else: # multiple gray images [N,H,W] 434 img = img[..., np.newaxis] 435 elif len(img_shape) != 4: 436 raise ValueError(f"Image(s) have wrong shape: {img_shape}") 437 438 n_images = img.shape[0] 439 aspect_ratio = img.shape[2] / img.shape[1] 440 rows = n_images // cols + int(n_images % cols > 0) 441 width = int(image_width * cols) 442 height = int(image_width * rows * aspect_ratio) 443 444 if not use_original_style: 445 plt_style = 'seaborn-v0_8' if 'seaborn-v0_8' in plt.style.available else 'classic' 446 plt.style.use(plt_style) 447 448 fig, axes = plt.subplots(nrows=rows, ncols=cols, figsize=(width, height)) 449 axes = np.array(axes).ravel() 450 fig.subplots_adjust(hspace=hspace, wspace=wspace) 451 452 if isinstance(title, str): 453 fig.suptitle(title, fontsize=20, y=0.95) 454 455 # Invert images if needed 456 if invert: 457 max_val = 2**(img.dtype.itemsize*8) - 1 458 img = max_val - img 459 460 for idx, ax in enumerate(axes): 461 if idx >= n_images: 462 ax.axis("off") 463 continue 464 cur_img = img[idx] 465 466 # Handle color spaces 467 used_cmap = cmap 468 if color_space.lower() == "bgr" and cur_img.shape[2] == 3: 469 cur_img = cv2.cvtColor(cur_img, cv2.COLOR_BGR2RGB) 470 used_cmap = None 471 elif color_space.lower() == "hsv" and cur_img.shape[2] == 3: 472 cur_img = cv2.cvtColor(cur_img, cv2.COLOR_HSV2RGB) 473 used_cmap = None 474 elif color_space.lower() in ["gray", "grey", "g"]: 475 if cur_img.shape[2] == 3: 476 cur_img = cv2.cvtColor(cur_img, cv2.COLOR_RGB2GRAY) 477 used_cmap = "gray" 478 479 if isinstance(title, (list, tuple)): 480 ax.set_title(title[idx], fontsize=12) 481 if not axis: 482 ax.axis("off") 483 484 ax.imshow(cur_img.squeeze(), cmap=used_cmap) 485 486 if save_to: 487 os.makedirs(os.path.dirname(save_to), exist_ok=True) 488 fig.savefig(save_to, dpi=300) 489 490 plt.show() 491 if not use_original_style: 492 plt.rcParams.update(original_style)
Display one or multiple images in a flexible and configurable grid.
This function supports multiple color spaces, automatic reshaping of input tensors, batch display, color inversion, and saving to disk.
Parameters:
- img (np.ndarray):
Input image or batch of images.
Accepted shapes:
[H, W], [H, W, C], [N, H, W], or [N, H, W, C].
- title (str or list[str], optional):
Overall or per-image titles.
- image_width (int, optional):
Width of each image in inches (default: 10).
- axis (bool, optional):
Whether to show axes (default: False).
- color_space (str, optional):
Color space of the image: "RGB", "BGR", "gray", or "HSV" (default: "RGB").
- cmap (str, optional):
Matplotlib colormap for grayscale images (default: None).
- cols (int, optional):
Number of columns in the subplot grid (default: 1).
- save_to (str, optional):
File path to save the figure (default: None).
- hspace (float, optional):
Vertical spacing between subplots (default: 0.2).
- wspace (float, optional):
Horizontal spacing between subplots (default: 0.2).
- use_original_style (bool, optional):
Keep current Matplotlib style if True (default: False).
- invert (bool, optional):
Invert color values (default: False).
Returns:
None
Example:
advanced_imshow(batch_images, cols=3, color_space="BGR", title="Predictions")
496def show_images(image_paths: list, title=None, image_width=5, axis=False, 497 color_space="gray", cmap=None, 498 cols=2, save_to=None, hspace=0.01, wspace=0.01, 499 use_original_style=False, invert=False): 500 """ 501 Load and display multiple images from disk using `advanced_imshow`. 502 503 Parameters: 504 - image_paths (list[str]): <br> 505 List of file paths to load. 506 - title (str or list[str], optional): <br> 507 Plot title(s). 508 - image_width (int, optional): <br> 509 Width of each image (default: 5). 510 - axis (bool, optional): <br> 511 Whether to display axes (default: False). 512 - color_space (str, optional): <br> 513 Color space to convert images to.<br> 514 One of: "gray", "rgb", "hsv", "bgr" (default: "gray"). 515 - cmap (str, optional): <br> 516 Colormap for grayscale images (default: None). 517 - cols (int, optional): <br> 518 Number of columns in the grid (default: 2). 519 - save_to (str, optional): <br> 520 Path to save the figure (default: None). 521 - hspace (float, optional): <br> 522 Vertical spacing between subplots (default: 0.01). 523 - wspace (float, optional): <br> 524 Horizontal spacing between subplots (default: 0.01). 525 - use_original_style (bool, optional): <br> 526 Keep current Matplotlib style (default: False). 527 - invert (bool, optional): <br> 528 Whether to invert images (default: False). 529 530 Returns: 531 np.ndarray: Loaded images stacked as an array. 532 533 Example: 534 >>> show_images(["img1.png", "img2.png"], color_space="rgb", cols=2) 535 """ 536 images = [] 537 for img_path in image_paths: 538 img = cv2.imread(img_path) 539 if color_space.lower() == "rgb": 540 img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) 541 elif color_space.lower() == "hsv": 542 img = cv2.cvtColor(img, cv2.COLOR_BGR2HSV) 543 elif color_space.lower() in ["gray", "grey", "g"]: 544 img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) 545 images.append(img) 546 547 images = np.array(images) 548 advanced_imshow(images, title=title, image_width=image_width, axis=axis, 549 color_space=color_space, cmap=cmap, cols=cols, save_to=save_to, 550 hspace=hspace, wspace=wspace, 551 use_original_style=use_original_style, invert=invert) 552 return images
Load and display multiple images from disk using advanced_imshow.
Parameters:
- image_paths (list[str]):
List of file paths to load.
- title (str or list[str], optional):
Plot title(s).
- image_width (int, optional):
Width of each image (default: 5).
- axis (bool, optional):
Whether to display axes (default: False).
- color_space (str, optional):
Color space to convert images to.
One of: "gray", "rgb", "hsv", "bgr" (default: "gray").
- cmap (str, optional):
Colormap for grayscale images (default: None).
- cols (int, optional):
Number of columns in the grid (default: 2).
- save_to (str, optional):
Path to save the figure (default: None).
- hspace (float, optional):
Vertical spacing between subplots (default: 0.01).
- wspace (float, optional):
Horizontal spacing between subplots (default: 0.01).
- use_original_style (bool, optional):
Keep current Matplotlib style (default: False).
- invert (bool, optional):
Whether to invert images (default: False).
Returns: np.ndarray: Loaded images stacked as an array.
Example:
show_images(["img1.png", "img2.png"], color_space="rgb", cols=2)
556def plot_image_with_values(img, block_size=8, cmap='gray', title=None, 557 font_size=6, save_to=None): 558 """ 559 Plot an image with annotated mean values over non-overlapping blocks. 560 561 Each block represents the mean pixel intensity of its region. The mean 562 values are displayed as text annotations directly on the image. 563 564 Parameters: 565 - img (np.ndarray): <br> 566 2D grayscale image (H, W) or 3D single-channel image (H, W, 1). 567 - block_size (int or tuple, optional): <br> 568 Size of each block (default: 8). 569 - cmap (str, optional): <br> 570 Matplotlib colormap (default: "gray"). 571 - title (str, optional): <br> 572 Plot title (default: None). 573 - font_size (int, optional): <br> 574 Font size of value annotations (default: 6). 575 - save_to (str, optional): <br> 576 Path to save the figure (default: None). 577 578 Returns:<br> 579 None 580 581 Example: 582 ```python 583 from imshow import plot_image_with_values 584 import cv2 585 586 img = cv2.imread('example.png', cv2.IMREAD_GRAYSCALE) 587 plot_image_with_values(img, block_size=16, cmap='gray', title='Mean Block Values', font_size=8) 588 ``` 589 590 Or: 591 ```python 592 fig, ax = plt.subplots(nrows=2, ncols=4, figsize=(4*4, 6)) 593 idx = 0 594 595 img_1 = plot(ax[0][0], path=input_samples[idx], title=f"Input", cmap="gray") 596 plot_image_with_values(img_1, block_size=16, ax=ax[1][0]) 597 598 img_2 = plot(ax[0][1], path=pred_model[idx], title=f"{model_name}") 599 plot_image_with_values(img_2, block_size=16, ax=ax[1][1]) 600 601 img_3 = plot(ax[0][2], path=real[idx], title=f"ground truth") 602 plot_image_with_values(img_3, block_size=16, ax=ax[1][2]) 603 604 img_4 = plot(ax[0][3], path=pred_model[idx], title=f"Difference", sub_image=real[idx]) 605 plot_image_with_values(img_4, block_size=16, ax=ax[1][3]) 606 607 plt.show() 608 ``` 609 """ 610 # Ensure 2D image 611 if img.ndim == 3 and img.shape[2] == 1: 612 img = img[:, :, 0] 613 elif img.ndim == 3 and img.shape[2] > 1: 614 raise ValueError("Only grayscale images are supported for block annotation.") 615 616 # Compute mean over blocks 617 mean_img = block_reduce(img, block_size=(block_size, block_size), func=np.mean) 618 max_value = mean_img.max() 619 620 # Plot 621 plt.figure(figsize=(mean_img.shape[1]/2, mean_img.shape[0]/2)) 622 plt.imshow(mean_img, cmap=cmap, interpolation='nearest') 623 plt.colorbar(label='Mean Value') 624 625 # Annotate each block 626 for i in range(mean_img.shape[0]): 627 for j in range(mean_img.shape[1]): 628 val = mean_img[i, j] 629 color = 'white' if val < max_value/1.5 else 'black' 630 plt.text(j, i, f'{val:.1f}', ha='center', va='center', color=color, fontsize=font_size) 631 632 plt.title(title or f'Mean Values over {block_size}x{block_size} Blocks') 633 plt.xticks([]) 634 plt.yticks([]) 635 plt.tight_layout() 636 637 if save_to: 638 os.makedirs(os.path.dirname(save_to), exist_ok=True) 639 plt.savefig(save_to, dpi=300) 640 641 plt.show()
Plot an image with annotated mean values over non-overlapping blocks.
Each block represents the mean pixel intensity of its region. The mean values are displayed as text annotations directly on the image.
Parameters:
- img (np.ndarray):
2D grayscale image (H, W) or 3D single-channel image (H, W, 1).
- block_size (int or tuple, optional):
Size of each block (default: 8).
- cmap (str, optional):
Matplotlib colormap (default: "gray").
- title (str, optional):
Plot title (default: None).
- font_size (int, optional):
Font size of value annotations (default: 6).
- save_to (str, optional):
Path to save the figure (default: None).
Returns:
None
Example:
from imshow import plot_image_with_values
import cv2
img = cv2.imread('example.png', cv2.IMREAD_GRAYSCALE)
plot_image_with_values(img, block_size=16, cmap='gray', title='Mean Block Values', font_size=8)
Or:
fig, ax = plt.subplots(nrows=2, ncols=4, figsize=(4*4, 6))
idx = 0
img_1 = plot(ax[0][0], path=input_samples[idx], title=f"Input", cmap="gray")
plot_image_with_values(img_1, block_size=16, ax=ax[1][0])
img_2 = plot(ax[0][1], path=pred_model[idx], title=f"{model_name}")
plot_image_with_values(img_2, block_size=16, ax=ax[1][1])
img_3 = plot(ax[0][2], path=real[idx], title=f"ground truth")
plot_image_with_values(img_3, block_size=16, ax=ax[1][2])
img_4 = plot(ax[0][3], path=pred_model[idx], title=f"Difference", sub_image=real[idx])
plot_image_with_values(img_4, block_size=16, ax=ax[1][3])
plt.show()
644def show_image_with_line_and_profile(imgs, axis='row', index=None, titles=None, figsize=(10, 4)): 645 """ 646 Display one or multiple grayscale images with a highlighted line (row or column) 647 and plot the corresponding pixel intensity profile below or beside each image. 648 649 Parameters: 650 - imgs (list[np.ndarray]):<br> 651 List of grayscale images to analyze. 652 - axis (str, optional):<br> 653 Direction of the line ("row" or "column") (default: "row"). 654 - index (int, optional): <br> 655 Index of the selected line. If None, the central line is used (default: None). 656 - titles (list[str], optional): <br> 657 Titles for each image (default: ["Image 1", "Image 2", ...]). 658 - figsize (tuple, optional): <br> 659 Figure size per image pair (default: (10, 4)). 660 661 Returns: 662 - list[np.ndarray]: <br> 663 List of pixel intensity profiles corresponding to the selected line in each image. 664 665 Example: 666 >>> show_image_with_line_and_profile( 667 ... imgs=[img_input, img_pred, img_gt], 668 ... axis="row", 669 ... titles=["Input", "Prediction", "Ground Truth"] 670 ... ) 671 """ 672 assert axis in ['row', 'column'], "axis must be 'row' or 'column'" 673 674 n_images = len(imgs) 675 if titles is None: 676 titles = [f"Image {i+1}" for i in range(n_images)] 677 line_values_list = [] 678 679 fig, axes = plt.subplots(n_images, 2, figsize=(figsize[0]*2, figsize[1]*n_images)) 680 681 # Ensure axes is 2D 682 if n_images == 1: 683 axes = np.expand_dims(axes, axis=0) 684 685 for i, (img, ax_pair, title) in enumerate(zip(imgs, axes, titles)): 686 h, w = img.shape 687 if index is None: 688 idx = h // 2 if axis == 'row' else w // 2 689 else: 690 idx = index 691 692 ax_img, ax_profile = ax_pair 693 694 # Show image with line 695 ax_img.imshow(img, cmap='gray', interpolation='nearest') 696 if axis == 'row': 697 ax_img.axhline(idx, color='red', linewidth=1) 698 line_values = img[idx, :] 699 else: 700 ax_img.axvline(idx, color='red', linewidth=1) 701 line_values = img[:, idx] 702 line_values_list.append(line_values) 703 704 ax_img.set_title(title) 705 ax_img.set_xticks([]) 706 ax_img.set_yticks([]) 707 708 # Plot pixel values along the line 709 x = np.arange(len(line_values)) 710 ax_profile.plot(x, line_values, color='black', linewidth=1) 711 ax_profile.set_title(f'Pixel values along {axis} {idx}') 712 ax_profile.set_xlabel('Pixel index') 713 ax_profile.set_ylabel('Intensity') 714 ax_profile.grid(True) 715 716 plt.tight_layout() 717 plt.show() 718 719 return line_values_list
Display one or multiple grayscale images with a highlighted line (row or column) and plot the corresponding pixel intensity profile below or beside each image.
Parameters:
- imgs (list[np.ndarray]):
List of grayscale images to analyze.
- axis (str, optional):
Direction of the line ("row" or "column") (default: "row").
- index (int, optional):
Index of the selected line. If None, the central line is used (default: None).
- titles (list[str], optional):
Titles for each image (default: ["Image 1", "Image 2", ...]).
- figsize (tuple, optional):
Figure size per image pair (default: (10, 4)).
Returns:
- list[np.ndarray]:
List of pixel intensity profiles corresponding to the selected line in each image.
Example:
show_image_with_line_and_profile( ... imgs=[img_input, img_pred, img_gt], ... axis="row", ... titles=["Input", "Prediction", "Ground Truth"] ... )