【python+opencv】实现数出细胞/硬币数量

1. 细胞原图/硬币原图:

(矩形作为干扰项)

 aa317d479a194caf9414552705ae1859.png

2. 运行结果图:

(不同的图 实现结果可能会有差异 可能需要修改参数等 此代码仅以例图为例)

5ef1ddc3fd4b417cad345bcee8837b70.png

3. 数细胞完整代码:

import cv2
import numpy as np

# (1)输出细胞个数;
# (2)画出每个细胞的编号;

# 读取图像
color_image = cv2.imread('img5.png')
gray_image = cv2.cvtColor(color_image, cv2.COLOR_BGR2GRAY)

# 高斯模糊,可能需要调整核大小
gray_image = cv2.GaussianBlur(gray_image, (23, 23), 0)

# 二值化处理
ret, thresh = cv2.threshold(gray_image, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)

thresh = cv2.adaptiveThreshold(gray_image, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY_INV, blockSize=33, C=22)

# 改用膨胀处理提高轮廓识别
kernel = np.ones((1, 1), np.uint8)
dilated_img = cv2.erode(thresh, kernel, iterations=2)

dilated_img = cv2.GaussianBlur(dilated_img, (13, 13), 0)

dilated_img = cv2.Canny(dilated_img, 50, 120)

# 查找并绘制轮廓
dilated_img = np.uint8(dilated_img)
contours, _ = cv2.findContours(dilated_img, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

cell_count = 0
marked_image = color_image.copy()


# 引入圆度计算函数
def circularity(cnt):
    perimeter = cv2.arcLength(cnt, True)
    area = cv2.contourArea(cnt)
    if perimeter == 0:  # 避免除以零错误
        return 0
    return 4 * np.pi * (area / (perimeter ** 2))


area_threshold = 50

# 圆度阈值
circularity_threshold = 0.7

for cnt in contours:
    area = cv2.contourArea(cnt)
    # 计算当前轮廓的圆度
    circle_ratio = circularity(cnt)
    # 只有当轮廓面积大于给定阈值且圆度足够高时才认为是cell
    if circle_ratio > circularity_threshold and area > area_threshold:
        cell_count += 1
        x, y, w, h = cv2.boundingRect(cnt)
        cv2.rectangle(marked_image, (x, y), (x + w, y + h), (0, 255, 0), 2)
        cv2.putText(marked_image, str(cell_count), (x + 7, y + 20), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 0, 255), 2)

print("细胞的数量为:", cell_count)
cv2.putText(marked_image, f"Cell Count: {cell_count}", (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 0), 2)
cv2.imshow('Cell Count', marked_image)
cv2.waitKey(0)
cv2.destroyAllWindows()

AI对该代码的解释:

这段代码使用了OpenCV库和NumPy来处理图像并识别其中的细胞,主要步骤如下:
1. 导入必要的库

  • cv2:OpenCV的Python接口。
  • numpy as np:用于数值计算和数组操作。
  • 2. 读取图像

  • 使用cv2.imread()读取名为img5.png的图像。
  • 转换彩色图像到灰度图像gray_image使用cv2.cvtColor()。
  • 3. 高斯模糊

  • 应用高斯模糊cv2.GaussianBlur()来减少噪声,核大小为(23, 23)。
  • 4. 二值化处理

  • 使用cv2.threshold()进行全局阈值处理,选择Otsu's二值化方法cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU。
  • 同时尝试使用自适应阈值处理cv2.adaptiveThreshold(),以适应图像中的不同光照条件。
  • 5. 形态学操作

  • 使用cv2.erode()进行腐蚀操作,核为np.ones((1, 1), np.uint8),迭代次数为2。
  • 再次应用高斯模糊cv2.GaussianBlur(),核大小为(13, 13)。
  • 使用cv2.Canny()边缘检测算法找到边缘。
  • 6. 查找轮廓

  • 将图像转换为np.uint8格式。
  • 使用cv2.findContours()找到所有外部轮廓。
  • 7. 轮廓筛选

  • 定义一个函数circularity()来计算轮廓的圆度。
  • 设定面积阈值area_threshold和圆度阈值circularity_threshold。
  • 遍历所有轮廓,只保留面积大于阈值且圆度高于阈值的轮廓。
  • 8. 标记细胞

  • 对于每个满足条件的轮廓,增加cell_count计数器。
  • 使用cv2.rectangle()和cv2.putText()在原图上标记细胞的位置和编号。
  • 9. 输出结果

  • 打印细胞总数。
  • 在图像上添加总细胞计数的文字标签。
  • 使用cv2.imshow()显示标记后的图像。
  • 等待按键事件cv2.waitKey(0),然后关闭所有窗口cv2.destroyAllWindows()。
  • 这个流程可以自动识别和计数图像中的细胞,通过调整参数(如模糊核大小、阈值、圆度阈值等)可以优化识别效果。

    4. 数硬币完整代码:

    import math
    import imutils
    from imutils import perspective
    from imutils import contours
    import numpy as np
    import cv2
    
    
    def midpoint(ptA, ptB):
        return (ptA[0] + ptB[0]) * 0.5, (ptA[1] + ptB[1]) * 0.5
    
    
    def DistEuclidean(ptA, ptB):
        return math.sqrt(math.pow((ptA[0] - ptB[0]), 2) + math.pow((ptA[1] - ptB[1]), 2))
    
    
    def is_coin(c):
        # 计算轮廓的周长
        peri = cv2.arcLength(c, True)
        # 获取轮廓的近似多边形,这里使用3%的周长容差
        approx = cv2.approxPolyDP(c, 0.03 * peri, True)
    
        # 检查近似后的顶点数是否接近圆形(对于较小的误差容忍,顶点数应接近于1)
        circularity = 4 * math.pi * cv2.contourArea(c) / (peri * peri)
        # 这里假设圆形轮廓的圆度大于0.8
        return 8 <= len(approx) <= 12 and circularity > 0.8
    
    
    # image = cv2.imread('img.png')
    image = cv2.imread('coin.png')
    
    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    gray = cv2.GaussianBlur(gray, (11, 11), 0)
    
    edged = cv2.Canny(gray, 50, 100)
    # cv2.imshow("Canny", edged)
    
    edged = cv2.dilate(edged, None, iterations=1)
    edged = cv2.erode(edged, None, iterations=1)
    
    cnts = cv2.findContours(edged.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    cnts = imutils.grab_contours(cnts)
    
    (cnts, _) = contours.sort_contours(cnts)
    pixelsPerMetric = 1
    
    coin_count = 0
    orig = image.copy()
    for c in cnts:
        # 检查轮廓是否可能是硬币
        if not is_coin(c):
            continue
        if cv2.contourArea(c) < 200:
            continue
        print("area:", cv2.contourArea(c))
    
        coin_count += 1
    
        x, y, w, h = cv2.boundingRect(c)
        cv2.rectangle(orig, (x, y), (x + w, y + h), (0, 255, 0))
        cv2.putText(orig, str(coin_count), (x + 7, y + 20), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 255, 255), 2)
        
    
    print("Total number of Coins:", coin_count)
    cv2.putText(orig, f"Coin Count: {coin_count}", (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2)
    cv2.imshow("Image", orig)
    cv2.waitKey(0)
    

    AI对该代码的解释:

    这段代码使用了OpenCV和imutils库来检测和识别图像中的硬币,并计算它们的数量。以下是代码的详细解释:
    1. 导入库

  • math:用于数学运算。
  • imutils:提供了实用的图像处理工具,如透视校正和轮廓排序。
  • numpy as np:用于数值计算和数组操作。
  • cv2:OpenCV库,用于图像处理和计算机视觉任务。
  • 2. 定义辅助函数

  • midpoint():计算两个点的中点。
  • DistEuclidean():计算两点之间的欧几里得距离。
  • is_coin(c):检查轮廓是否可能是硬币,通过计算轮廓的周长和近似多边形的顶点数,以及轮廓的圆度。
  • 3. 读取图像

  • 使用cv2.imread()读取图像coin.png。
  • 4. 预处理图像

  • 转换图像为灰度图cv2.cvtColor()。
  • 应用高斯模糊cv2.GaussianBlur()减少噪声。
  • 5. 边缘检测

  • 使用Canny边缘检测cv2.Canny()。
  • 进行膨胀和腐蚀操作cv2.dilate()和cv2.erode()以增强边缘。
  • 6. 查找轮廓

  • 使用cv2.findContours()找到图像中的所有轮廓。
  • 调整轮廓顺序imutils.grab_contours()。
  • 7. 轮廓筛选和标记

  • 遍历所有轮廓,使用is_coin()函数筛选可能是硬币的轮廓。
  • 排除面积过小的轮廓。
  • 绘制矩形框cv2.rectangle()并标注硬币编号cv2.putText()。
  • 8. 统计硬币数量

  • 每当检测到一个硬币轮廓时,增加coin_count计数器。
  • 9. 输出结果

  • 打印总的硬币数量。
  • 在图像上添加硬币总数的文字标签cv2.putText()。
  • 显示最终图像cv2.imshow(),等待按键事件cv2.waitKey(0),然后关闭所有窗口。
  • 二者代码差别不大,我就是互相改的,可能会有coin的代码中出现cell的单词哈哈哈哈哈哈

    作者:青鱼鱼

    物联沃分享整理
    物联沃-IOTWORD物联网 » 【python+opencv】实现数出细胞/硬币数量

    发表回复