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

Python基于OpenCV实现重叠发丝准确计数的解决方案咨询

下图为待处理的原始图像,我的目标是统计图像中的发丝数量,本示例中实际发丝数量为5根。
原始图像

请问是否有相关函数或思路可以实现该需求?以下是我目前的实现步骤,可供参考。

步骤1:对比度拉伸(Contrast Stretching)

我首先提升了图像的对比度,并将处理后的对比度增强图像写入指定目录。

# 映射每个灰度值到输出灰度值
def pixelVal(pix, r1, s1, r2, s2):
    if (0 <= pix and pix <= r1):
        return (s1 / r1)*pix
    elif (r1 < pix and pix <= r2):
        return ((s2 - s1)/(r2 - r1)) * (pix - r1) + s1
    else:
        return ((255 - s2)/(255 - r2)) * (pix - r2) + s2
img = cv2.imread('./Hair image/Micro/Input/Test (5).jpg')

# 定义参数
r1 = 255*0.3
s1 = 255*0.1
r2 = 255*0.85
s2 = 255*1

# 向量化函数,应用到Numpy数组的每个值
pixelVal_vec = np.vectorize(pixelVal)

contrast_stretched = pixelVal_vec(img, r1, s1, r2, s2)
cv2.imwrite('./Hair Image/Micro/Contrast/contrast_test.png',contrast_stretched)

处理结果如下图所示:
对比度增强后图像

步骤2:图像锐化(Sharpen The Image)
# 创建用于图像锐化的内核,近似二阶导数,是一个较强的锐化内核
kernel = np.array([[1, 1, 1], [1, -8, 1], [1, 1, 1]], dtype=np.float32)
imgLaplacian = cv2.filter2D(opening, cv2.CV_32F, kernel)
sharp = np.float32(opening)
imgResult = sharp - imgLaplacian

# 转换回8位灰度图
imgResult = np.clip(imgResult, 0, 255)
imgResult = imgResult.astype('uint8')
imgLaplacian = np.clip(imgLaplacian, 0, 255)
imgLaplacian = np.uint8(imgLaplacian)

# 转换为HSV格式并提取v通道
hsv = cv2.cvtColor(imgResult, cv2.COLOR_BGR2HSV)
v_channel = hsv[:, :, 2]
步骤3:图像二值化(Otsu Thresholding)

将锐化后的图像转换为二值图像:

median_blur = cv2.medianBlur(v_channel,5)
ret4,th4 = cv2.threshold(median_blur,0,255,cv2.THRESH_BINARY_INV+cv2.THRESH_OTSU)

本阶段处理结果如下图所示:
二值化后图像

步骤5:图像膨胀(Dilate the image)
close_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (7, 5))
dilate_v2 = cv2.dilate(th4,close_kernel,iterations = 3)

处理结果:
膨胀后图像

步骤6:距离变换(Distance Transform)
dist = cv2.distanceTransform(dilate_v2, cv2.DIST_L2, cv2.DIST_MASK_PRECISE)
th, peaks  = cv2.threshold(dist, 0, 255, cv2.THRESH_BINARY)
peaks8u = cv2.convertScaleAbs(peaks)
contours, hierarchy = cv2.findContours(peaks8u, cv2.RETR_CCOMP, cv2.CHAIN_APPROX_SIMPLE)
peaks8u = cv2.convertScaleAbs(peaks)    # 用作掩模

for i in range(len(contours)):
    x, y, w, h = cv2.boundingRect(contours[i])
    _, mx, _, mxloc = cv2.minMaxLoc(dist[y:y+h, x:x+w], peaks8u[y:y+h, x:x+w])
    cv2.rectangle(img, (x, y), (x+w, y+h), (0, 255, 255), 2)

cv2.imshow('Detect', img)
cv2.waitKey(0)

距离变换检测结果
该方法最终检测出了9个矩形框,和实际的5根发丝不符,且重叠的发丝会被识别为1根而非2根。因此我尝试了另一种效果更好的方案,但重叠线段被误计为1根的问题仍然存在。

替代方案:连通域统计(ConnectedComponentsWithStat)
output = cv2.connectedComponentsWithStats(peaks8u, connectivity = 4, ltype = cv2.CV_32S)
(numLabels, labels, stats, centroids) = output

# 过滤连通域
# 初始化输出掩模,存储所有提取出的发丝区域
mask = np.zeros(peaks8u.shape, dtype="uint8")

# 遍历所有唯一的连通域标签,跳过第一个标签(标签0为背景)
for i in range(1, numLabels):
    # 提取当前标签对应的连通域统计信息
    x = stats[i, cv2.CC_STAT_LEFT]
    y = stats[i, cv2.CC_STAT_TOP]
    w = stats[i, cv2.CC_STAT_WIDTH]
    h = stats[i, cv2.CC_STAT_HEIGHT]
    area = stats[i, cv2.CC_STAT_AREA]
    
    # 筛选宽、高、面积都在合理区间的连通域
    keepWidth = w > 55 and w < 1281
    keepHeight = h > 10  and h < 721
    keepArea = area > 200 and area < 80000
    
    # 保留通过所有筛选条件的连通域
    if all((keepWidth, keepHeight, keepArea)):
        print("[INFO] 保留连通域 '{}'".format(i))
        componentMask = (labels == i).astype("uint8") * 255
        mask = cv2.bitwise_or(mask, componentMask)
        cv2.imshow("Image", img)
        cv2.imshow("Characters", mask)
        cv2.waitKey(0)
        cv2.destroyAllWindows()

该方案的处理结果如下:
连通域检测结果1
连通域检测结果2
本次共识别出4根发丝,但重叠的发丝仍被误统计为1根,结果不准确。

请问是否有相关函数或思路可以实现发丝数量的准确统计?谢谢!

备注:我想知道用于统计图像中小圆形的计算机视觉方案是否适用于该场景。


内容的提问来源于stack exchange,提问作者Crazy

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.08 16:15:03