使用 OpenCV 在彩色背景上进行边缘检测
Edge detection on colored background using OpenCV
我正在使用以下代码检测给定文档的边缘。
private Mat edgeDetection(Mat src) {
Mat edges = new Mat();
Imgproc.cvtColor(src, edges, Imgproc.COLOR_BGR2GRAY);
Imgproc.GaussianBlur(edges, edges, new Size(5, 5), 0);
Imgproc.Canny(edges, edges, 10, 30);
return edges;
}
然后我可以从这个 edges
中找到最大的轮廓来找到文档。
我的问题是我可以从下面的图片中找到文档:
但不是来自以下图片:
如何改进此边缘检测?
在 OpenCV 中有一个名为 dilate
的函数,这会使线条变暗。所以试试下面的代码。
private Mat edgeDetection(Mat src) {
Mat edges = new Mat();
Imgproc.cvtColor(src, edges, Imgproc.COLOR_BGR2GRAY);
Imgproc.dilate(edges, edges, Imgproc.getStructuringElement(Imgproc.MORPH_RECT, new Size(10, 10)));
Imgproc.GaussianBlur(edges, edges, new Size(5, 5), 0);
Imgproc.Canny(edges, edges, 15, 15 * 3);
return edges;
}
我用的是Python,但是主要思路是一样的
如果你直接对img2做cvtColor: bgr -> gray,那你一定会失败。因为灰色变得难以区分区域:
相关回答:
- How to detect colored patches in an image using OpenCV?
- OpenCV C++/Obj-C: Detecting a sheet of paper / Square Detection
在您的图像中,纸张为 white
,而背景为 colored
。所以,最好检测论文是HSV color space
中的Saturation(饱和度)
频道。 HSV参考https://en.wikipedia.org/wiki/HSL_and_HSV#Saturation.
主要步骤:
- 读入
BGR
- 将图像从
bgr
转换为 hsv
space
- S 通道阈值
- 然后求最大外轮廓(或者做
Canny
,或者HoughLines
,我选findContours
),近似得到角点。
这是第一个结果:
这是第二个结果:
Python代码(Python 3.5 + OpenCV 3.3):
#!/usr/bin/python3
# 2017.12.20 10:47:28 CST
# 2017.12.20 11:29:30 CST
import cv2
import numpy as np
##(1) read into bgr-space
img = cv2.imread("test2.jpg")
##(2) convert to hsv-space, then split the channels
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
h,s,v = cv2.split(hsv)
##(3) threshold the S channel using adaptive method(`THRESH_OTSU`) or fixed thresh
th, threshed = cv2.threshold(s, 50, 255, cv2.THRESH_BINARY_INV)
##(4) find all the external contours on the threshed S
cnts = cv2.findContours(threshed, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)[-2]
canvas = img.copy()
#cv2.drawContours(canvas, cnts, -1, (0,255,0), 1)
## sort and choose the largest contour
cnts = sorted(cnts, key = cv2.contourArea)
cnt = cnts[-1]
## approx the contour, so the get the corner points
arclen = cv2.arcLength(cnt, True)
approx = cv2.approxPolyDP(cnt, 0.02* arclen, True)
cv2.drawContours(canvas, [cnt], -1, (255,0,0), 1, cv2.LINE_AA)
cv2.drawContours(canvas, [approx], -1, (0, 0, 255), 1, cv2.LINE_AA)
## Ok, you can see the result as tag(6)
cv2.imwrite("detected.png", canvas)
我正在使用以下代码检测给定文档的边缘。
private Mat edgeDetection(Mat src) {
Mat edges = new Mat();
Imgproc.cvtColor(src, edges, Imgproc.COLOR_BGR2GRAY);
Imgproc.GaussianBlur(edges, edges, new Size(5, 5), 0);
Imgproc.Canny(edges, edges, 10, 30);
return edges;
}
然后我可以从这个 edges
中找到最大的轮廓来找到文档。
我的问题是我可以从下面的图片中找到文档:
但不是来自以下图片:
如何改进此边缘检测?
在 OpenCV 中有一个名为 dilate
的函数,这会使线条变暗。所以试试下面的代码。
private Mat edgeDetection(Mat src) {
Mat edges = new Mat();
Imgproc.cvtColor(src, edges, Imgproc.COLOR_BGR2GRAY);
Imgproc.dilate(edges, edges, Imgproc.getStructuringElement(Imgproc.MORPH_RECT, new Size(10, 10)));
Imgproc.GaussianBlur(edges, edges, new Size(5, 5), 0);
Imgproc.Canny(edges, edges, 15, 15 * 3);
return edges;
}
我用的是Python,但是主要思路是一样的
如果你直接对img2做cvtColor: bgr -> gray,那你一定会失败。因为灰色变得难以区分区域:
相关回答:
- How to detect colored patches in an image using OpenCV?
- OpenCV C++/Obj-C: Detecting a sheet of paper / Square Detection
在您的图像中,纸张为 white
,而背景为 colored
。所以,最好检测论文是HSV color space
中的Saturation(饱和度)
频道。 HSV参考https://en.wikipedia.org/wiki/HSL_and_HSV#Saturation.
主要步骤:
- 读入
BGR
- 将图像从
bgr
转换为hsv
space - S 通道阈值
- 然后求最大外轮廓(或者做
Canny
,或者HoughLines
,我选findContours
),近似得到角点。
这是第一个结果:
这是第二个结果:
Python代码(Python 3.5 + OpenCV 3.3):
#!/usr/bin/python3
# 2017.12.20 10:47:28 CST
# 2017.12.20 11:29:30 CST
import cv2
import numpy as np
##(1) read into bgr-space
img = cv2.imread("test2.jpg")
##(2) convert to hsv-space, then split the channels
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
h,s,v = cv2.split(hsv)
##(3) threshold the S channel using adaptive method(`THRESH_OTSU`) or fixed thresh
th, threshed = cv2.threshold(s, 50, 255, cv2.THRESH_BINARY_INV)
##(4) find all the external contours on the threshed S
cnts = cv2.findContours(threshed, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)[-2]
canvas = img.copy()
#cv2.drawContours(canvas, cnts, -1, (0,255,0), 1)
## sort and choose the largest contour
cnts = sorted(cnts, key = cv2.contourArea)
cnt = cnts[-1]
## approx the contour, so the get the corner points
arclen = cv2.arcLength(cnt, True)
approx = cv2.approxPolyDP(cnt, 0.02* arclen, True)
cv2.drawContours(canvas, [cnt], -1, (255,0,0), 1, cv2.LINE_AA)
cv2.drawContours(canvas, [approx], -1, (0, 0, 255), 1, cv2.LINE_AA)
## Ok, you can see the result as tag(6)
cv2.imwrite("detected.png", canvas)