在 OpenCV (Java) 中增强边缘,同时去除噪声

Enhance edges in OpenCV (Java) while removing noise

我正在尝试在 Java 中编写一个 OpenCV 程序,该程序拍摄带有 phone 的标记的照片并在标记上找到圆形轮廓。我已经让它在 Android 模拟器(照片具有完美的照明条件)中工作,但无法让它与 phone 本身一起工作。 这是我要捕获的标记:

在使用转换为灰度、高斯模糊和 Canny 边缘检测器的组合后,我得到了这个输出:

如果我随后尝试在图像上找到轮廓,返回的轮廓数量确实很多(超过 1000 个),但它们并没有闭合(因为边缘似乎太弱了。 在原始图像上绘制的轮廓:

这是我用于分割的代码:

Mat processed = new Mat(original.height(), original.width(), original.type());

Imgproc.cvtColor(original, processed, Imgproc.COLOR_RGB2GRAY);

Imgproc.GaussianBlur(processed, processed, new Size(7, 7), 0.1);

Imgproc.Canny(processed, processed,  50, 50*3, 3, false);

我尝试调整不同的参数(阈值等),但感觉这不是理想的解决方案。我在想必须有一种方法来增强 canny 检测器返回的边缘,但我自己还没有找到任何东西。

感谢任何帮助!

您可以查看 dilation 在 opencv 中的操作以增强边缘。

https://docs.opencv.org/4.x/db/df6/tutorial_erosion_dilatation.html

另请查看下面的示例,其中包含 Canny 边缘检测、approxPolyDPminEnclosingCircle。非常接近你的问题。

https://docs.opencv.org/4.x/da/d0c/tutorial_bounding_rects_circles.html

https://docs.opencv.org/4.x/d3/dc0/group__imgproc__shape.html#ga0012a5fdaea70b8a9970165d98722b4c

https://docs.opencv.org/4.x/d3/dc0/group__imgproc__shape.html#ga8ce13c24081bbc7151e9326f412190f1

你想要这样的东西吗?

噪声通常存在 - 它可以减少 - 你可以用 adaptiveThreshold 区分圆圈,然后使用我下面描述的方法找到圆圈。关键是,您从相机获得的真实世界图像可能包含一大堆其他圆圈 - 因此最好找到所有圆圈。比较它们的大小。找出在颜色、大小和位置方面与您的标记最相似的 4 个圆圈。

快速python区分圆圈的代码:

# Make a gray version
gry = cv2.cvtColor(im, cv2.COLOR_BGR2GRAY)

# Thresh
ada = cv2.adaptiveThreshold(
    gry, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 11, 2
)

# Remove noises
out = cv2.medianBlur(ada, 7)

# Invert colors (Not needed)
out = ~out

我测试了 Python 代码并且它有效;您可以找到 Java 或 C++ 的等价物。我试图解释 Java 代码,但我是临时写的,没有测试它。我写的 Java 代码可能有错误,但它确实抓住了要点。稍微改变一下可能会起作用。我还编写了应该将圆圈作为最后一个块的代码。使用它很棘手,需要调整参数。

Java:

...
Imgproc.cvtColor(im, gry, Imgproc.COLOR_RGBA2GRAY);
Imgproc.adaptiveThreshold(gry, ada, 255, 
    Imgproc.ADAPTIVE_THRESH_MEAN_C, Imgproc.THRESH_BINARY, 11, 2);
Imgproc.medianBlur(ada, out, 7);
...

寻找圈子:
Java:

...
SimpleBlobDetector_Params params = new SimpleBlobDetector_Params();
params.set_filterByCircularity(true);
params.set_minCircularity(0.01f);
params.set_maxArea(50000);//important
SimpleBlobDetector detector = SimpleBlobDetector.create(params);

// List of detected points 
MatOfKeyPoint keyPoints = new MatOfKeyPoint();
detector.detect(ada, keyPoints);

// Draw circles on final image
Scalar color = new Scalar(127, 0, 255);
for (KeyPoint key: keyPoints.toList()) {
    Imgproc.circle(im, key.pt, (int) (key.size / 2.0f), color, 3/*Thickness*/);
}
...