Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

测量对象属性

一旦在二值图像中识别出对象(例如,通过连通分量标记),我们就可以从每个对象中提取定量测量值或属性。这些属性对于对象分类、表征和进一步的图像分析至关重要。它们使我们能够以精确的数学方式描述对象的几何形状。

scikit-image 库中,skimage.measure.regionprops 函数是实现此目的的强大工具。它以标记图像作为输入(其中每个对象都有一个唯一的整数标签),并返回每个标记区域的属性列表。本节详细介绍了一些可以测量的最常见属性。

面积

对象的面积是属于它的像素总数。对于二值对象 OO,其数学定义为:

Area(O)=(x,y)O1\text{Area}(O) = \sum_{(x,y) \in O} 1

它是衡量对象大小的最基本尺度。


边界框

边界框是完全包围对象的最小矩形,其边与图像轴平行。它由其左上角(min_row, min_col)和右下角(max_row, max_col)的坐标定义。这对于在图像中定位对象很有用。


质心

质心是对象的几何中心或质心。对于具有 NN 个像素,坐标为 (xi,yi)(x_i, y_i) 的对象,质心 (Cx,Cy)(C_x, C_y) 计算为坐标的平均值:

Cx=1Ni=1Nxi,Cy=1Ni=1NyiC_x = \frac{1}{N} \sum_{i=1}^{N} x_i, \quad C_y = \frac{1}{N} \sum_{i=1}^{N} y_i

质心提供一个单点来表示对象的位置。请注意,对于非凸形状,质心可能位于对象本身之外。


离心率

离心率是衡量对象偏离完美圆形的程度。通过将一个具有相同二阶矩的椭圆拟合到对象上来计算。离心率是该椭圆的离心率,其值在0到1之间。

离心率=1λ短轴2λ长轴2\text{离心率} = \sqrt{1 - \frac{\lambda_{\text{短轴}}^2}{\lambda_{\text{长轴}}^2}}

其中 λ短轴\lambda_{\text{短轴}}λ长轴\lambda_{\text{长轴}} 分别是椭圆的短轴和长轴长度。离心率为0对应于圆形,而接近1的值对应于线段。这对于区分紧凑和细长形状很有用。

离心率图示。红色椭圆与蓝色对象具有相同的二阶矩。对象的离心率定义为该椭圆的离心率。

离心率图示。红色椭圆与蓝色对象具有相同的二阶矩。对象的离心率定义为该椭圆的离心率。


坚实度

坚实度衡量对象的紧凑性。它是对象面积与其凸包面积之比。凸包是包含对象所有点的最小凸多边形。

坚实度=面积凸包面积\text{坚实度} = \frac{\text{面积}}{\text{凸包面积}}

一个没有凹陷的实心对象的坚实度为1。边界复杂或有孔洞的对象的坚实度值较低。


为清晰起见,图像中对象的凸包用深灰色表示。

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.patches as mpatches
from skimage.measure import label, regionprops
from skimage.morphology import label as skimage_label

# 创建一个包含各种形状的示例二值图像
image = np.zeros((200, 200), dtype=np.uint8)
image[10:60, 10:60] = 1   # 正方形
image[10:60, 70:180] = 1  # 矩形
image[70:120, 10:60] = 1  # 另一个正方形
image[80:110, 20:50] = 0   # 在其中制造一个洞
image[130:180, 80:190] = 1 # 一个更大的区域
image[140:170, 90:120] = 0 # 带有一个洞
image[145:165, 130:180] = 1 # 和一个突出部分

# 标记连通分量
labeled_image = skimage_label(image)

# 测量属性
regions = regionprops(labeled_image)

# 显示图像并叠加属性
fig, ax = plt.subplots(1, 1, figsize=(10, 10))
ax.imshow(labeled_image, cmap='nipy_spectral')

for props in regions:
    y0, x0 = props.centroid
    orientation = props.orientation
    x1 = x0 + np.cos(orientation) * 0.5 * props.major_axis_length
    y1 = y0 - np.sin(orientation) * 0.5 * props.major_axis_length
    x2 = x0 - np.sin(orientation) * 0.5 * props.minor_axis_length
    y2 = y0 - np.cos(orientation) * 0.5 * props.minor_axis_length

    ax.plot((x0, x1), (y0, y1), '-r', linewidth=2.5)
    ax.plot((x0, x2), (y0, y2), '-r', linewidth=2.5)
    ax.plot(x0, y0, '.g', markersize=15)

    minr, minc, maxr, maxc = props.bbox
    bx = (minc, maxc, maxc, minc, minc)
    by = (minr, minr, maxr, maxr, minr)
    ax.plot(bx, by, '-b', linewidth=2.5)

    print(f"区域 {props.label}:")
    print(f"  面积: {props.area}")
    print(f"  质心: ({y0:.2f}, {x0:.2f})")
    print(f"  离心率: {props.eccentricity:.2f}")
    print(f"  坚实度: {props.solidity:.2f}")
    
ax.axis('off')
plt.tight_layout()
plt.show()