【问题标题】:Low contrast stops HoughCircles from detection(?)低对比度会阻止 HoughCircles 检测(?)
【发布时间】:2022-01-03 22:23:23
【问题描述】:

我正在尝试构建一个脚本,能够计算图片中有多少欧元(现在只是硬币)。为了做到这一点,我正在考虑首先定位硬币,然后比较它们的相对大小,以便了解每个硬币的价值,就像我在其他地方看到的那样。我的难点在于第一步,图像的预处理。 需要注意的是,只有当背景和某些硬币之间的对比度非常低时才会出现此问题

我已经尝试了各种方法预处理与不同的检测方法,如 connectedComponentsWithStats()、findContours() 和 SimpleBlobDetector,但我实现的最成功的组合是:

import numpy as np
import cv2
import os

path = 'GenericImages/TP2/'
path_coins_highlighted = 'GenericImages/Highlights'
path_gaussian_blurs = 'GenericImages/Gaussian_Blurs'
dirs = os.listdir(path)
i = 0
for file in dirs:
    path2img = os.path.join(path, file)
    img = cv2.imread(path2img)

    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

    # clahe = cv2.createCLAHE(clipLimit=40, tileGridSize=(8, 8))
    # equalized = clahe.apply(gray)

    gray_blur = cv2.GaussianBlur(gray, (15, 15), 0)
    # gray_blur = cv2.bilateralFilter(gray, 9, 65, 9)

    circles = cv2.HoughCircles(gray_blur, cv2.HOUGH_GRADIENT, 1, 15, param1=50, param2=30, minRadius=0, maxRadius=0)
    circles = np.uint16(np.around(circles))
    for x in circles[0, :]:
        cv2.circle(img, (x[0], x[1]), x[2], (0, 255, 0), 2)
        cv2.circle(img, (x[0], x[1]), 2, (0, 0, 255), 3)

    cv2.imshow('Gray', gray)
    cv2.imshow('Gaussian Blur', gray_blur)
    path_save_gaussian_blur = os.path.join(path_gaussian_blurs, str(i) + '_gaussian_blur.jpg')
    cv2.imwrite(path_save_gaussian_blur, gray_blur)
    # cv2.imshow('equalized', equalized)
    cv2.imshow('Highlights', img)
    path_save_highlights = os.path.join(path_coins_highlighted, str(i) + '_highlight.jpg')
    cv2.imwrite(path_save_highlights, img)
    i += 1
    cv2.waitKey(0)

问题在于检测的一致性,我相信当它失败时,它会这样做是因为背景和 HoughCircles 没有检测到的硬币之间几乎没有对比。下面的一组图像显示了算法失败的情况。

设置 0:

SET1:

我尝试使用均衡和具有不同参数的双边滤波器进行调整,以消除噪音但保留过渡区域(硬币的轮廓),但我没有发现显着的改进。

如果我应该寻找解决此问题的方向或想法,我将不胜感激。

【问题讨论】:

  • 试试sobel/canny参数,是否提取右边缘。

标签: python opencv computer-vision contrast


【解决方案1】:

照明不均匀,您的图像很小且压缩严重。这是阻碍良好检测的两个因素。控制照明可能很困难,但至少要确保使用无损图像格式(例如png)以避免压缩伪影。

无论如何,您的非均匀照明使这成为称为增益划分的照明标准化方法的一个很好的例子。这个想法是您尝试构建背景模型,然后通过该模型对每个输入像素进行加权。在图像的大部分时间里,输出增益应该是相对恒定的。这非常有用,因为如果我们消除非均匀照明,我们可以为硬币创建一个前景蒙版,然后我们只需近似圆形到硬币的轮廓。

让我们试一试:

# imports:
import cv2
import numpy as np

# image path
path = "D://opencvImages//"
fileName = "FHlbm.jpg"

# Reading an image in default mode:
inputImage = cv2.imread(path + fileName)
# Deep copy for results:
inputImageCopy = inputImage.copy()

# Get local maximum:
kernelSize = 30
maxKernel = cv2.getStructuringElement(cv2.MORPH_RECT, (kernelSize, kernelSize))
localMax = cv2.morphologyEx(inputImage, cv2.MORPH_CLOSE, maxKernel, None, None, 1, cv2.BORDER_REFLECT101)

# Perform gain division
gainDivision = np.where(localMax == 0, 0, (inputImage / localMax))

# Clip the values to [0,255]
gainDivision = np.clip((255 * gainDivision), 0, 255)

# Convert the mat type from float to uint8:
gainDivision = gainDivision.astype("uint8")

cv2.imshow("Gain Division", gainDivision)
cv2.waitKey(0)

产量:

这是对第一张图像应用增益分割的结果。请注意,现在背景几乎是统一的。这非常棒,因为我们可以应用简单的自动阈值来创建仅包含前景对象的二进制掩码,如下所示:

# Convert RGB to grayscale:
grayscaleImage = cv2.cvtColor(gainDivision, cv2.COLOR_BGR2GRAY)

# Get binary image via Otsu:
_, binaryImage = cv2.threshold(grayscaleImage, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)

这是二值图像:

现在,我们这里有个问题。压缩伪影使此蒙版嘈杂。我们可以应用一点morphology 来改进二进制 blob,但是您的图像非常小,所以我跳过了这一步。如果您可以访问更大的无损图像,则可能需要包括清理步骤。

现在,我将简单地尝试计算每个大于阈值的 blob 的最小封闭圆,我应该得到比 Hough 的更可靠的检测。让我们看看:

# Find the circle blobs on the binary mask:
contours, hierarchy = cv2.findContours(binaryImage, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
contoursPoly = [None] * len(contours)

# Store the circles here:
detectedCircles = []

# Alright, just look for the outer bounding boxes:
for i, c in enumerate(contours):

    # Get blob area:
    blobArea = cv2.contourArea(c)
    print(blobArea)

    # Set min area:
    minArea = 100

    # Process only big blobs:
    if blobArea > minArea:

        # Approximate the contour to a circle:
        (x, y), radius = cv2.minEnclosingCircle(c)

        # Compute the center and radius:
        center = (int(x), int(y))
        radius = int(radius)

        # Draw the circles:
        cv2.circle(inputImageCopy, center, radius, (0, 0, 255), 1)
        cv2.line(inputImageCopy, center, center, (0, 255, 0), 2)

        # Store the center and radius:
        detectedCircles.append([center, radius])

cv2.imshow("Circles", inputImageCopy)
cv2.waitKey(0)

让我们看看在原始图像的深层副本上绘制的结果:

还不错。所有圆的数据(中心和半径)都存储在detectedCircles 列表中。我们可以这样打印信息:

# Check out the detected circles:
for i in range(len(detectedCircles)):
    center, r = detectedCircles[i]
    print("Circle #: "+str(i)+" x: "+str(center[0])+" y: "+str(center[1])+" r: "+str(r))

【讨论】:

  • 感谢您对您的想法和方法的详细解释,它使查找您提到的内容变得更容易,它确实很有意义,我迫不及待地想稍后尝试一下,然后我会给出一些反馈
  • 除法增益之前的均衡对您有意义吗?如果硬币包含低对比度和/或仍然包含大量反射的情况,您会推荐什么?这种情况下的问题是,阈值处理几乎完全去除了那些硬币,只留下了几个像素,甚至没有形成形状(例如来自 set0 的图片)
猜你喜欢
  • 2020-12-16
  • 2020-11-18
  • 1970-01-01
  • 2013-06-02
  • 2013-12-09
  • 1970-01-01
  • 1970-01-01
  • 2017-07-12
  • 1970-01-01
相关资源
最近更新 更多