You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用scikit与skimage分割痣图像为两半以分析对称性

Analyzing Mole Image Symmetry by Splitting Rotated Blob into Halves

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.08 15:27:32