寻找轮廓的边缘强度/大小以排除模糊物体

3
我想使用Python为灰度图像中的每个对象找到边缘强度值。我通过阈值检测来检测对象以创建二进制图像,然后使用OpenCV findContours找到边缘。我在每个图像中检测到的某些对象模糊不清,我想根据轮廓的边缘梯度的大小来排除它们(请参见下面的图像,其中显示了一个聚焦的对象和一个模糊的对象的示例)。如何处理每个轮廓的边缘强度,以给出每个对象的边缘强度值,以便我可以根据某个阈值排除模糊不清的对象?
contours, hierarchy = cv2.findContours(binary_img, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
edges = cv2.drawContours(original_image, contours, -1, (255, 255, 255), 1)

我使用上述代码在经过阈值处理生成的二进制图像上绘制边缘,并将检测到的对象发送到处理过程中,但我希望排除模糊的对象,因为它们不需要进一步分析。下面的图片展示了绘制出边缘的图像,我想找到描述每个对象从每个边缘像素到平均边缘梯度的一些值,并且只进一步处理那些边缘幅度高于某个阈值的对象,即处于聚焦状态的对象。
原始图像: Original Image 带有边缘的图像: Image with Edges

你能添加一些中间输出(轮廓、边缘图像)和代码示例吗? - venkata krishnan
在黑色背景上绘制白色轮廓,并将其用作掩模以获取其下方图像的值。然后对每个掩模轮廓的像素值进行平均。 - fmw42
3个回答

2
这是一个可能的方法:
  • 将图像转换为灰度
  • 自适应阈值以获取二进制图像
  • 膨胀以增强轮廓
  • 查找轮廓并提取ROI
  • 进行拉普拉斯变化以检测模糊

我们首先将其转换为灰度并进行自适应阈值处理。

enter image description here

注意:保留html标签。
import cv2
import numpy as np

image = cv2.imread('1.jpg')

result = image.copy()
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
thresh = cv2.threshold(gray, 0, 255,cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)[1]

最初的回答:
接下来,我们进行膨胀以增强轮廓。

输入图像描述

kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5,5))
dilate = cv2.dilate(thresh, kernel, iterations=3)

现在我们要找到轮廓并提取每个ROI。我们使用Laplacian的变化来对这个ROI轮廓进行模糊检测。最初的回答为:

现在我们要找到轮廓并提取每个ROI。我们使用Laplacian的变化来对这个ROI轮廓进行模糊检测。

cv2.Laplacian(image, cv2.CV_64F).var()

基本上,我们将图像的单个通道与以下3x3卷积核卷积,并取响应的标准差的平方。如果方差低于定义的阈值,则ROI模糊,否则ROI不模糊。请查看此博客文章以获取更多详细信息。"最初的回答"
[0  1  0]
[1 -4  1]
[0  1  0]

这是结果

enter image description here

ROI编号:1,价值:27.655757845590053

ROI编号:2,价值:7.385658155007905

(注:ROI即投资回报率)

ROI_num = 0
for c in cnts:
    x,y,w,h = cv2.boundingRect(c)
    ROI = image[y:y+h, x:x+w]
    value = cv2.Laplacian(ROI, cv2.CV_64F).var()  
    cv2.rectangle(result, (x, y), (x + w, y + h), (36,255,12), 2)
    cv2.putText(result, "{0:.2f}".format(value), (x,y - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (36,255,12), 2)
    cv2.imshow("ROI_{}".format(ROI_num), ROI)
    ROI_num += 1
    
    print('ROI_Number: {}, Value: {}'.format(ROI_num, value))

以下是另一张图片的结果: 输入图像描述 输入图像描述 输入图像描述

ROI_Number: 1, Value: 23.96665214233842

ROI_Number: 2, Value: 67.59560601952461


完整代码
import cv2
import numpy as np

image = cv2.imread('1.jpg')

result = image.copy()
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
thresh = cv2.threshold(gray, 0, 255,cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)[1]
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5,5))
dilate = cv2.dilate(thresh, kernel, iterations=3)

cnts = cv2.findContours(dilate, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]

ROI_num = 0
for c in cnts:
    x,y,w,h = cv2.boundingRect(c)
    ROI = image[y:y+h, x:x+w]
    value = cv2.Laplacian(ROI, cv2.CV_64F).var()  
    cv2.rectangle(result, (x, y), (x + w, y + h), (36,255,12), 2)
    cv2.putText(result, "{0:.2f}".format(value), (x,y - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (36,255,12), 2)
    cv2.imshow("ROI_{}".format(ROI_num), ROI)
    ROI_num += 1
    
    print('ROI_Number: {}, Value: {}'.format(ROI_num, value))

cv2.imshow('thresh', thresh)
cv2.imshow('dilate', dilate)
cv2.imshow('result', result)
cv2.waitKey(0)

为什么要用拉普拉斯算子?这并不反映梯度强度。 - user1196549
这是一种在显微镜明场自动对焦的硅藻比较研究中详细介绍的模糊检测方法。据我理解,该方法有效的原因是拉普拉斯算子突出了图像中包含快速强度变化的区域,因此如果边缘和非边缘的方差都很高,则代表着一个清晰的图像。同样地,如果方差很低,则图像中几乎没有边缘。图像越模糊,边缘就越少。基本上,拉普拉斯算子可用于边缘检测。 - nathancy

1
如果我理解你的问题正确,你可以尝试使用一些导数核来得到一些阈值。例如,[-1 0 1] 核可能有效。此外,您可以检查 Canny 边缘检测算法,也许它会有帮助。

0
一个简单的方法是计算梯度幅值图像,并在检测到的轮廓线上或整个斑点内部评估梯度。可能更好的方法是在轮廓线膨胀后得到的环形区域内进行评估。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接