如何用scikit与skimage分割痣图像为两半以分析对称性
Looks like you've already nailed the preprocessing steps for your mole image—great work getting to the rotated blob stage! Let's walk through splitting this blob into vertical/horizontal halves and comparing them for symmetry, using only skimage and matplotlib as you requested.
Step 1: Fix Orientation Calculation & Re-Binarize Rotated Image
First, let's correct the orientation conversion (your original code had a small math typo) and re-binarize the rotated image. skimage.transform.rotate outputs floating-point values instead of strict binary, so this step ensures clean segmentation:
# Get orientation from region props and convert radians to degrees props = regionprops(img_blob.astype(int))[0] ang = -props.orientation * 180 / pi # Correct conversion to degrees img_blob_rotated = transform.rotate(img_blob, ang) # Re-binarize to get back strict 0/1 values img_rotated_binary = img_blob_rotated > 0.5
Step 2: Split into Left/Right Halves
Grab the image dimensions, find the vertical midpoint, and pad the smaller half if the width is odd to ensure both halves are identical in size:
height, width = img_rotated_binary.shape mid_col = width // 2 left_half = img_rotated_binary[:, :mid_col] right_half = img_rotated_binary[:, mid_col:] # Pad right half if width is odd if width % 2 != 0: right_half = np.pad(right_half, ((0,0), (0,1)), mode='constant')
Step 3: Split into Top/Bottom Halves
Repeat the process for horizontal splitting, padding the bottom half if the height is odd:
mid_row = height // 2 top_half = img_rotated_binary[:mid_row, :] bottom_half = img_rotated_binary[mid_row:, :] # Pad bottom half if height is odd if height % 2 != 0: bottom_half = np.pad(bottom_half, ((0,1), (0,0)), mode='constant')
Step 4: Quantify Symmetry
Use two practical methods to measure symmetry:
- Pixel Matching: Percentage of identical pixels between a half and its mirrored counterpart.
- SSIM: Structural Similarity Index, which accounts for texture and structural consistency (more robust than raw pixel matching).
from skimage.metrics import structural_similarity as ssim # Left/right symmetry mirrored_left = np.fliplr(left_half) right_pixel_match = np.mean(mirrored_left == right_half) right_ssim_score = ssim(mirrored_left.astype(float), right_half.astype(float)) # Top/bottom symmetry mirrored_top = np.flipud(top_half) bottom_pixel_match = np.mean(mirrored_top == bottom_half) bottom_ssim_score = ssim(mirrored_top.astype(float), bottom_half.astype(float)) # Print results print("Symmetry Analysis Results:") print(f"Left-Right Pixel Match: {right_pixel_match:.2%}") print(f"Left-Right SSIM Score: {right_ssim_score:.4f}") print(f"Top-Bottom Pixel Match: {bottom_pixel_match:.2%}") print(f"Top-Bottom SSIM Score: {bottom_ssim_score:.4f}")
Step 5: Visualize the Splits
Plot the rotated blob alongside its halves and mirrored counterparts to see symmetry visually:
fig, axes = plt.subplots(2, 3, figsize=(12, 8)) # Original rotated blob axes[0,0].imshow(img_rotated_binary, cmap='gray') axes[0,0].set_title("Rotated Binary Blob") axes[0,0].axis('off') # Left half and mirrored left axes[0,1].imshow(left_half, cmap='gray') axes[0,1].set_title("Left Half") axes[0,1].axis('off') axes[0,2].imshow(mirrored_left, cmap='gray') axes[0,2].set_title("Mirrored Left") axes[0,2].axis('off') # Top half and mirrored top axes[1,0].imshow(top_half, cmap='gray') axes[1,0].set_title("Top Half") axes[1,0].axis('off') axes[1,1].imshow(mirrored_top, cmap='gray') axes[1,1].set_title("Mirrored Top") axes[1,1].axis('off') axes[1,2].imshow(bottom_half, cmap='gray') axes[1,2].set_title("Bottom Half") axes[1,2].axis('off') plt.tight_layout() plt.show()
Full Combined Code
Here's the complete code integrating your preprocessing with the symmetry analysis:
import matplotlib.pyplot as plt import numpy as np from skimage import io, color, filters, morphology as morph, transform, metrics from skimage.measure import regionprops from math import pi IMG_NAME = "first_mole.png" img = io.imread(IMG_NAME) # Read image img_gray = color.rgb2gray(img) # Convert to grayscale # Binary image using Otsu's threshold thres = filters.threshold_otsu(img_gray) img_binary = (img_gray < thres) # Remove small objects and holes img_binary = morph.remove_small_objects(img_binary, min_size=64) img_blob = morph.remove_small_holes(img_binary, area_threshold=64) plt.imshow(img_blob, cmap='gray') plt.title("Processed Blob") plt.axis('off') plt.show() # Rotate blob to align with vertical axis props = regionprops(img_blob.astype(int))[0] ang = -props.orientation * 180 / pi img_blob_rotated = transform.rotate(img_blob, ang) plt.imshow(img_blob_rotated, cmap='gray') plt.title("Rotated Blob") plt.axis('off') plt.show() # ------------------- Symmetry Analysis ------------------- # Re-binarize rotated image img_rotated_binary = img_blob_rotated > 0.5 # Split left/right halves height, width = img_rotated_binary.shape mid_col = width // 2 left_half = img_rotated_binary[:, :mid_col] right_half = img_rotated_binary[:, mid_col:] if width % 2 != 0: right_half = np.pad(right_half, ((0,0), (0,1)), mode='constant') # Split top/bottom halves mid_row = height // 2 top_half = img_rotated_binary[:mid_row, :] bottom_half = img_rotated_binary[mid_row:, :] if height % 2 != 0: bottom_half = np.pad(bottom_half, ((0,1), (0,0)), mode='constant') # Calculate symmetry scores mirrored_left = np.fliplr(left_half) right_pixel_match = np.mean(mirrored_left == right_half) right_ssim_score = ssim(mirrored_left.astype(float), right_half.astype(float)) mirrored_top = np.flipud(top_half) bottom_pixel_match = np.mean(mirrored_top == bottom_half) bottom_ssim_score = ssim(mirrored_top.astype(float), bottom_half.astype(float)) # Print results print("Symmetry Analysis Results:") print(f"Left-Right Pixel Match: {right_pixel_match:.2%}") print(f"Left-Right SSIM Score: {right_ssim_score:.4f}") print(f"Top-Bottom Pixel Match: {bottom_pixel_match:.2%}") print(f"Top-Bottom SSIM Score: {bottom_ssim_score:.4f}") # Visualize splits fig, axes = plt.subplots(2, 3, figsize=(12, 8)) axes[0,0].imshow(img_rotated_binary, cmap='gray') axes[0,0].set_title("Rotated Blob") axes[0,0].axis('off') axes[0,1].imshow(left_half, cmap='gray') axes[0,1].set_title("Left Half") axes[0,1].axis('off') axes[0,2].imshow(mirrored_left, cmap='gray') axes[0,2].set_title("Mirrored Left") axes[0,2].axis('off') axes[1,0].imshow(top_half, cmap='gray') axes[1,0].set_title("Top Half") axes[1,0].axis('off') axes[1,1].imshow(mirrored_top, cmap='gray') axes[1,1].set_title("Mirrored Top") axes[1,1].axis('off') axes[1,2].imshow(bottom_half, cmap='gray') axes[1,2].set_title("Bottom Half") axes[1,2].axis('off') plt.tight_layout() plt.show()
A quick note: The SSIM score ranges from 0 (no similarity) to 1 (perfect symmetry), so higher values mean more symmetric halves.
内容的提问来源于stack exchange,提问作者FranceFord

