检测网格的角
Detect corners of grid
我正在尝试检测我必须处理的各种图片中的网格角。图片可能会歪斜,有些可能方向相对较好,但我们不能保证所有图片都是这样。
为了确定网格的角,我尝试使用霍夫线但无济于事。有时霍夫线不能识别网格的边缘,很难判断画出的线哪些属于网格边缘,哪些是网格线。
然后我决定使用轮廓来检测网格的边缘。然而,它在许多轮廓上拾取并导致相同的问题,即在所有已识别的轮廓中识别哪些在拐角处。
为了帮助解决这个问题,我使用了双边过滤、Canny 边缘检测、形态扩张和 Harris 边缘检测,正如在与我的问题类似的问题中看到的那样。即使采取了所有这些措施,我仍然会遇到大量假角,有时甚至无法识别真角。
我想知道是否有人可以帮助我改进角点检测的结果,或者是否有人有完全不同的建议可能有助于解决我的问题。目标是获得角落,以便我可以使用 10 X 10 网格执行单应性以解决图像中的倾斜问题。它还有助于将网格正方形映射到像素 space,这非常有用。
这是我的代码(命名有点草率,但我稍后会尝试修复)。另外,是的,我全力进行了双边过滤,它似乎有助于去除不必要的轮廓和角落。
当我尝试将霍夫线应用到我的等高线图像时,我似乎也遇到了一个错误:
error: (-215) img.type() == (((0) & ((1 << 3) - 1)) + (((1)-1) << 3)) in function cv::HoughLinesStandard
from PIL import Image
import numpy as np
import cv2
import glob
#import images using opencv
images = [cv2.imread(file) for file in glob.glob("SpAMImages/*.jpg")]
for image in images:
#resizes image gotten from folder and performs bilateral filtering
img = cv2.bilateralFilter(cv2.resize(image, (715,715)), 15, 800, 800)
#applies a canny edge detection filter on the images loaded from the folder
gridEdges = cv2.Canny(img, 140, 170)
#apply image dilation
kernel = np.ones((5,5), np.uint8)
gridEdges = cv2.dilate(gridEdges, kernel, iterations=1)
gridEdges = np.float32(gridEdges)
gridEdges = cv2.blur(gridEdges,(10,10))
gridEdges = cv2.cornerHarris(gridEdges,2,3,0.04)
gridEdges = cv2.dilate(gridEdges,None)
img[gridEdges>0.01*gridEdges.max()]=[0,0,255]
#draw contours on current image
imgray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ret, thresh = cv2.threshold(imgray, 127, 255, 0)
contourImage, contours, hierarchy =
cv2.findContours(thresh,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)
contour = cv2.drawContours(img, contours, -1, (0,255,0), 3)
'''
def largest_4_sided_contour(thresh, show_contours=True):
contourImage, contours, hierarchy =
cv2.findContours(thresh,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)
contour = cv2.drawContours(img, contours, -1, (0,255,0), 3)
contours = sorted(contours, key = cv2.contourArea, reverse = True)
for cnt in contours[:min(5, len(contours))]:
print(len(cnt))
if len(cnt) == 4:
return cnt
return None
print(largest_4_sided_contour(thresh))
#applies a hough transformation to extract gridlines from the image
-----------THIS LINE BELOW GIVES ME THE ERROR-----------------
lines = cv2.HoughLines(img, 1, np.pi/180, 245)
#iterates through an array of lines gottne from the hough transform
#and draws them unto the image
for i in range(len(lines)):
for rho,theta in lines[i]:
a = np.cos(theta)
b = np.sin(theta)
x0 = a * rho
y0 = b * rho
x1 = int(x0 + 1000*(-b))
y1 = int(y0 + 1000*(a))
x2 = int(x0 - 1000*(-b))
y2 = int(y0 - 1000*(a))
cv2.line(img, (x1,y1),(x2,y2),(0,0,255),2)
cv2.imwrite('houghlines.jpg', img)
'''
#resize window because for some reason they are too large.
cv2.namedWindow('image', cv2.WINDOW_NORMAL)
cv2.resizeWindow('image', 800, 800)
#display all the images produced from above processes
cv2.imshow('image', img)
cv2.imshow('dilated edges', gridEdges)
#cv2.imshow('contour', contour)
cv2.waitKey(0)
'''
retrieve image size from imported image.
testImageWidth, testImageHeight = img.shape[:2]
print(testImageHeight, testImageWidth)'''
这些是我尝试使用轮廓检测和 Harris 角点检测获取角点时获得的一些图像。
使用轮廓和哈里斯角点检测识别角点:
以及我必须处理的一些图像示例。
主要示例网格:
有些偏斜的网格:
提前感谢您的帮助!!!
您正在 Python 使用 OpenCV,但我将使用 MATLAB 和 DIPimage 给您一个答案。我希望这个答案是关于概念的,而不是关于代码的。我确信有一些方法可以在 Python 中使用 OpenCV 完成所有这些事情。
我的目标是找到棋盘的四个角。网格本身可以猜到,因为它只是板的等距划分,没有必要尝试检测所有线。四个角给出了关于透视变换的所有信息。
检测板子最简单的方法就是识别板子是浅色的,底色是深色的。从灰度图像开始,我应用了一个小的闭合(我使用了一个直径为 7 像素的圆,这适用于我用作示例的下采样图像,但您可能希望适当增加尺寸全尺寸图像)。这给出了这个结果:
接下来,我使用 Otsu 阈值 selection 进行二值化,并去除空洞(这部分并不重要,如果有空洞,其余部分也可以)。我们现在看到的连接组件对应于电路板和相邻的电路板(或电路板周围的任何其他白色东西)。
选择最大的连通分量是一个相当常见的过程。在下面的代码中,我标记了图像(标识连接的组件),计算每个连接组件的像素数,select 像素最多的那个。
最后,从这个结果中减去它的侵蚀只剩下棋盘边缘的像素(这里以蓝色覆盖在输入图像上):
我用来寻找角点的技巧相当简单,但在这里失败了,因为其中一个角点不在图像中。在这四个边上使用 Hough 可能是一种更可靠的方法。使用 获得一些想法和代码,了解如何实现这一点。
无论如何,我发现板的左上角是最接近图像左上角的边缘像素。其他 3 个角也是如此。这些结果就是上图中的红点。
此处的第三个选项是将轮廓转换为多边形,使用 Douglas–Peucker 算法对其进行简化,丢弃沿着图像边缘的边缘(这是图像中不存在角的地方),以及延长其两侧的两条边以找到图像外部的顶点。
MATLAB(DIPimage)代码如下。
img = readim('https://i.stack.imgur.com/GYZGa.jpg');
img = colorspace(img,'gray');
% Downsample, makes display easier
img = gaussf(img,2);
img = img(0:4:end,0:4:end);
% Simplify and binarize
sim = closing(img,7);
brd = threshold(sim); % uses Otsu threshold selection
% Fill the holes
brd = fillholes(brd);
% Keep only the largest connected component
brd = label(brd);
msr = measure(brd);
[~,I] = max(msr,'size');
brd = brd == msr(I).id;
% Extract edges
brd = brd - erosion(brd,3,'rectangular');
% Find corners
pts = findcoord(brd);
[~,top_left] = min(sum(pts.^2,2));
[~,top_right] = min(sum((pts-[imsize(brd,1),0]).^2,2));
[~,bottom_left] = min(sum((pts-[0,imsize(brd,2)]).^2,2));
[~,bottom_right] = min(sum((pts-[imsize(brd,1),imsize(brd,2)]).^2,2));
% Make an image with corner pixels set
cnr = newim(brd,'bin');
cnr(pts(top_left,1),pts(top_left,2)) = 1;
cnr(pts(top_right,1),pts(top_right,2)) = 1;
cnr(pts(bottom_left,1),pts(bottom_left,2)) = 1;
cnr(pts(bottom_right,1),pts(bottom_right,2)) = 1;
cnr = dilation(cnr,3);
% Save images
writeim(sim,'so1.png')
out = overlay(img,brd,[0,0,255]);
out = overlay(out,cnr,[255,0,0]);
writeim(out,'so2.png')
我有一些答案要给你,虽然不完整,但可能对你有所帮助。
我使用 Ramer–Douglas–Peucker algorithm 确定轮廓,然后从轮廓中提取矩形框。然后我使用“框”区域与图像区域的百分比来删除较小的框。这将删除大部分垃圾箱。
这是我在 python 代码中所做的示例:
寻找轮廓:
def findcontours(self):
logging.info("Inside findcontours Contours...")
# Pre-process image
imgGray = self.imgProcess.toGrey(self.img)
logging.info("Success on converting image to greyscale")
imgThresh = self.imgProcess.toBinary(imgGray)
logging.info("Success on converting image to binary")
logging.info("Finding contours...")
image, contours, hierarchy = cv2.findContours(imgThresh.copy(), cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
logging.info("Contours found: %d", len(contours))
return contours
使用轮廓查找框:
def getRectangles(self, contours):
arrrect = []
imgArea = self.getArea()
logging.info("Image Area is: %d", imgArea)
for cnt in contours:
epsilon = 0.01*cv2.arcLength(cnt, True)
approx = cv2.approxPolyDP(cnt, epsilon, False)
area = cv2.contourArea(approx)
rect = cv2.minAreaRect(approx)
box = cv2.boxPoints(rect)
box = np.int0(box)
percentage = (area * 100) / imgArea
if percentage > 0.3:
arrrect.append(box)
return arrrect
结合这两种方法:
def process(self):
logging.info("Processing image...")
self.shape_handler = ShapeHandler(self.img)
contours = self.shape_handler.findcontours()
logging.info("Finding Rectangles from contours...")
rectangles = self.shape_handler.getRectangles(contours)
img = self.imgDraw.draw(self.img, rectangles, "Green", 10)
cv2.drawContours(img, array, -1, (0,255,0), thickness)
self.display(img)
logging.info("Amount of Rectangles Found: %d", len(rectangles))
显示图像:
def display(self, img):
cv2.namedWindow('image', cv2.WINDOW_NORMAL)
cv2.imshow("image", img)
cv2.waitKey(0)
cv2.destroyAllWindows()
最后一步是合并任何相交的框,因为您只对 edges/corners 感兴趣,然后只得到面积最大的框。查看 here 检查如何组合框。
我的编码来源:OpenCV 3.1 Documentation
你的图片结果:
正常:
偏斜:
希望对您有所帮助!
我正在尝试检测我必须处理的各种图片中的网格角。图片可能会歪斜,有些可能方向相对较好,但我们不能保证所有图片都是这样。
为了确定网格的角,我尝试使用霍夫线但无济于事。有时霍夫线不能识别网格的边缘,很难判断画出的线哪些属于网格边缘,哪些是网格线。
然后我决定使用轮廓来检测网格的边缘。然而,它在许多轮廓上拾取并导致相同的问题,即在所有已识别的轮廓中识别哪些在拐角处。
为了帮助解决这个问题,我使用了双边过滤、Canny 边缘检测、形态扩张和 Harris 边缘检测,正如在与我的问题类似的问题中看到的那样。即使采取了所有这些措施,我仍然会遇到大量假角,有时甚至无法识别真角。
我想知道是否有人可以帮助我改进角点检测的结果,或者是否有人有完全不同的建议可能有助于解决我的问题。目标是获得角落,以便我可以使用 10 X 10 网格执行单应性以解决图像中的倾斜问题。它还有助于将网格正方形映射到像素 space,这非常有用。
这是我的代码(命名有点草率,但我稍后会尝试修复)。另外,是的,我全力进行了双边过滤,它似乎有助于去除不必要的轮廓和角落。
当我尝试将霍夫线应用到我的等高线图像时,我似乎也遇到了一个错误:
error: (-215) img.type() == (((0) & ((1 << 3) - 1)) + (((1)-1) << 3)) in function cv::HoughLinesStandard
from PIL import Image
import numpy as np
import cv2
import glob
#import images using opencv
images = [cv2.imread(file) for file in glob.glob("SpAMImages/*.jpg")]
for image in images:
#resizes image gotten from folder and performs bilateral filtering
img = cv2.bilateralFilter(cv2.resize(image, (715,715)), 15, 800, 800)
#applies a canny edge detection filter on the images loaded from the folder
gridEdges = cv2.Canny(img, 140, 170)
#apply image dilation
kernel = np.ones((5,5), np.uint8)
gridEdges = cv2.dilate(gridEdges, kernel, iterations=1)
gridEdges = np.float32(gridEdges)
gridEdges = cv2.blur(gridEdges,(10,10))
gridEdges = cv2.cornerHarris(gridEdges,2,3,0.04)
gridEdges = cv2.dilate(gridEdges,None)
img[gridEdges>0.01*gridEdges.max()]=[0,0,255]
#draw contours on current image
imgray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ret, thresh = cv2.threshold(imgray, 127, 255, 0)
contourImage, contours, hierarchy =
cv2.findContours(thresh,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)
contour = cv2.drawContours(img, contours, -1, (0,255,0), 3)
'''
def largest_4_sided_contour(thresh, show_contours=True):
contourImage, contours, hierarchy =
cv2.findContours(thresh,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)
contour = cv2.drawContours(img, contours, -1, (0,255,0), 3)
contours = sorted(contours, key = cv2.contourArea, reverse = True)
for cnt in contours[:min(5, len(contours))]:
print(len(cnt))
if len(cnt) == 4:
return cnt
return None
print(largest_4_sided_contour(thresh))
#applies a hough transformation to extract gridlines from the image
-----------THIS LINE BELOW GIVES ME THE ERROR-----------------
lines = cv2.HoughLines(img, 1, np.pi/180, 245)
#iterates through an array of lines gottne from the hough transform
#and draws them unto the image
for i in range(len(lines)):
for rho,theta in lines[i]:
a = np.cos(theta)
b = np.sin(theta)
x0 = a * rho
y0 = b * rho
x1 = int(x0 + 1000*(-b))
y1 = int(y0 + 1000*(a))
x2 = int(x0 - 1000*(-b))
y2 = int(y0 - 1000*(a))
cv2.line(img, (x1,y1),(x2,y2),(0,0,255),2)
cv2.imwrite('houghlines.jpg', img)
'''
#resize window because for some reason they are too large.
cv2.namedWindow('image', cv2.WINDOW_NORMAL)
cv2.resizeWindow('image', 800, 800)
#display all the images produced from above processes
cv2.imshow('image', img)
cv2.imshow('dilated edges', gridEdges)
#cv2.imshow('contour', contour)
cv2.waitKey(0)
'''
retrieve image size from imported image.
testImageWidth, testImageHeight = img.shape[:2]
print(testImageHeight, testImageWidth)'''
这些是我尝试使用轮廓检测和 Harris 角点检测获取角点时获得的一些图像。
使用轮廓和哈里斯角点检测识别角点:
以及我必须处理的一些图像示例。
主要示例网格:
有些偏斜的网格:
提前感谢您的帮助!!!
您正在 Python 使用 OpenCV,但我将使用 MATLAB 和 DIPimage 给您一个答案。我希望这个答案是关于概念的,而不是关于代码的。我确信有一些方法可以在 Python 中使用 OpenCV 完成所有这些事情。
我的目标是找到棋盘的四个角。网格本身可以猜到,因为它只是板的等距划分,没有必要尝试检测所有线。四个角给出了关于透视变换的所有信息。
检测板子最简单的方法就是识别板子是浅色的,底色是深色的。从灰度图像开始,我应用了一个小的闭合(我使用了一个直径为 7 像素的圆,这适用于我用作示例的下采样图像,但您可能希望适当增加尺寸全尺寸图像)。这给出了这个结果:
接下来,我使用 Otsu 阈值 selection 进行二值化,并去除空洞(这部分并不重要,如果有空洞,其余部分也可以)。我们现在看到的连接组件对应于电路板和相邻的电路板(或电路板周围的任何其他白色东西)。
选择最大的连通分量是一个相当常见的过程。在下面的代码中,我标记了图像(标识连接的组件),计算每个连接组件的像素数,select 像素最多的那个。
最后,从这个结果中减去它的侵蚀只剩下棋盘边缘的像素(这里以蓝色覆盖在输入图像上):
我用来寻找角点的技巧相当简单,但在这里失败了,因为其中一个角点不在图像中。在这四个边上使用 Hough 可能是一种更可靠的方法。使用
无论如何,我发现板的左上角是最接近图像左上角的边缘像素。其他 3 个角也是如此。这些结果就是上图中的红点。
此处的第三个选项是将轮廓转换为多边形,使用 Douglas–Peucker 算法对其进行简化,丢弃沿着图像边缘的边缘(这是图像中不存在角的地方),以及延长其两侧的两条边以找到图像外部的顶点。
MATLAB(DIPimage)代码如下。
img = readim('https://i.stack.imgur.com/GYZGa.jpg');
img = colorspace(img,'gray');
% Downsample, makes display easier
img = gaussf(img,2);
img = img(0:4:end,0:4:end);
% Simplify and binarize
sim = closing(img,7);
brd = threshold(sim); % uses Otsu threshold selection
% Fill the holes
brd = fillholes(brd);
% Keep only the largest connected component
brd = label(brd);
msr = measure(brd);
[~,I] = max(msr,'size');
brd = brd == msr(I).id;
% Extract edges
brd = brd - erosion(brd,3,'rectangular');
% Find corners
pts = findcoord(brd);
[~,top_left] = min(sum(pts.^2,2));
[~,top_right] = min(sum((pts-[imsize(brd,1),0]).^2,2));
[~,bottom_left] = min(sum((pts-[0,imsize(brd,2)]).^2,2));
[~,bottom_right] = min(sum((pts-[imsize(brd,1),imsize(brd,2)]).^2,2));
% Make an image with corner pixels set
cnr = newim(brd,'bin');
cnr(pts(top_left,1),pts(top_left,2)) = 1;
cnr(pts(top_right,1),pts(top_right,2)) = 1;
cnr(pts(bottom_left,1),pts(bottom_left,2)) = 1;
cnr(pts(bottom_right,1),pts(bottom_right,2)) = 1;
cnr = dilation(cnr,3);
% Save images
writeim(sim,'so1.png')
out = overlay(img,brd,[0,0,255]);
out = overlay(out,cnr,[255,0,0]);
writeim(out,'so2.png')
我有一些答案要给你,虽然不完整,但可能对你有所帮助。 我使用 Ramer–Douglas–Peucker algorithm 确定轮廓,然后从轮廓中提取矩形框。然后我使用“框”区域与图像区域的百分比来删除较小的框。这将删除大部分垃圾箱。
这是我在 python 代码中所做的示例:
寻找轮廓:
def findcontours(self):
logging.info("Inside findcontours Contours...")
# Pre-process image
imgGray = self.imgProcess.toGrey(self.img)
logging.info("Success on converting image to greyscale")
imgThresh = self.imgProcess.toBinary(imgGray)
logging.info("Success on converting image to binary")
logging.info("Finding contours...")
image, contours, hierarchy = cv2.findContours(imgThresh.copy(), cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
logging.info("Contours found: %d", len(contours))
return contours
使用轮廓查找框:
def getRectangles(self, contours):
arrrect = []
imgArea = self.getArea()
logging.info("Image Area is: %d", imgArea)
for cnt in contours:
epsilon = 0.01*cv2.arcLength(cnt, True)
approx = cv2.approxPolyDP(cnt, epsilon, False)
area = cv2.contourArea(approx)
rect = cv2.minAreaRect(approx)
box = cv2.boxPoints(rect)
box = np.int0(box)
percentage = (area * 100) / imgArea
if percentage > 0.3:
arrrect.append(box)
return arrrect
结合这两种方法:
def process(self):
logging.info("Processing image...")
self.shape_handler = ShapeHandler(self.img)
contours = self.shape_handler.findcontours()
logging.info("Finding Rectangles from contours...")
rectangles = self.shape_handler.getRectangles(contours)
img = self.imgDraw.draw(self.img, rectangles, "Green", 10)
cv2.drawContours(img, array, -1, (0,255,0), thickness)
self.display(img)
logging.info("Amount of Rectangles Found: %d", len(rectangles))
显示图像:
def display(self, img):
cv2.namedWindow('image', cv2.WINDOW_NORMAL)
cv2.imshow("image", img)
cv2.waitKey(0)
cv2.destroyAllWindows()
最后一步是合并任何相交的框,因为您只对 edges/corners 感兴趣,然后只得到面积最大的框。查看 here 检查如何组合框。
我的编码来源:OpenCV 3.1 Documentation
你的图片结果:
正常:
偏斜:
希望对您有所帮助!