【问题标题】:Detect corners of grid检测网格的角落
【发布时间】:2018-07-01 01:48:04
【问题描述】:

我正在尝试检测我必须处理的各种图片中的网格角。图像可能会倾斜,有些可能相对较好的方向,但我们不能保证所有图像都会这样。

为了确定网格的角,我尝试使用霍夫线但无济于事。有时霍夫线不能识别网格的边缘,很难确定绘制的线中哪些属于网格边缘,哪些是网格线。

然后我决定使用轮廓来检测网格的边缘。然而,它会检测到许多轮廓,并导致识别所有已识别轮廓中哪些位于拐角处的问题。

为了帮助解决这个问题,我使用了双边过滤、Canny 边缘检测、形态膨胀和 Harris 边缘检测,正如在与我的问题类似的问题中看到的那样。即使在应用了所有这些措施之后,我仍然会得到大量的假角,并且有时无法识别出真角。

我想知道是否有人有办法让我改进角点检测的结果,或者是否有人有完全不同的建议可能有助于解决我的问题。目标是获得角落,以便我可以使用 10 X 10 网格执行单应性,以解决图像中的倾斜问题。它还有助于将网格正方形映射到像素空间,这非常有用。

这是我的代码(命名有点草率,但我稍后会尝试修复它)。另外,是的,我全力以赴进行双边过滤,它似乎有助于消除不必要的轮廓和角落。

当我尝试将霍夫线应用于轮廓图像时,我似乎也遇到了一个错误:

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 角点检测获得角点的一些图像。

使用轮廓和哈里斯角检测识别角:

还有一些我必须使用的图像示例。

主要示例网格:

有点倾斜的网格:

提前感谢您的帮助!!!

【问题讨论】:

  • 结合你的轮廓和霍夫线的方法应该能够提供帮助。只需将轮廓本身(未填充,只是轮廓)绘制到空白图像上。在 this 图像上使用 Hough 变换应该会给你四行。然后你可以简单地找到这四条线相交的角。请参阅我的回答 here 了解更多信息。
  • 哇,谢谢,您的回答解决了我在确定角落时遇到的很多问题。我会尝试你的建议并更新结果。
  • 酷,祝你好运! Lmk 如果你有问题。此外,您可以查看我构建的一系列霍夫线相关函数here on GitHub,这可能会有所帮助。我没有时间在这里写一个实际的解决方案,但如果你真的让它工作,回答你自己的问题也不错,这样其他人可能会偶然发现它! :) 此外,在轮廓上使用霍夫线的好处是,即使拐角在图像之外,您仍然可以计算实际的拐角。
  • 看到您的编辑错误,霍夫线仅适用于 the docs 中所述的 8 位图像,因此您可能正在向其中发送浮动图像或其他内容?
  • @AlexanderReynolds 嘿 :))我使用了您链接的答案中的霍夫线解决方案,它运行良好,但我遇到了一些问题。对于我尝试处理的某些图像,我不断收到运行时警告,因此必须关闭程序。即使集群位于角落,从集群中获得的一些中心也会出现在边缘的中间。我猜它可能是从两个不同的集群中绘制中心?如果有帮助,我可以编辑我的问题以更好地解释我在说什么。

标签: python opencv image-processing opencv-contour corner-detection


【解决方案1】:

您正在使用 OpenCV 在 Python 中工作,但我将使用带有 DIPimage 的 MATLAB 为您提供答案。我打算这个答案是关于概念的,而不是关于代码的。我确信有办法在 Python 中使用 OpenCV 完成所有这些事情。

我的目标是找到棋盘的四个角。网格本身是可以猜到的,因为它只是棋盘的等距划分,没有必要尝试检测所有的线。四个角给出了关于透视变换的所有信息。

检测电路板的最简单方法是识别它是浅色的并且具有深色背景。从灰度值图像开始,我应用了一个小的闭合(我使用了一个直径为 7 像素的圆圈,这适用于我用作示例的下采样图像,但您可能需要适当地增加尺寸以适应全尺寸图像)。这给出了这个结果:

接下来,我使用 Otsu 阈值选择进行二值化,并删除孔(那部分并不重要,如果也有孔,其余部分也可以使用)。我们现在看到的连接组件对应于电路板和相邻的电路板(或电路板周围的任何其他白色物体)。

选择最大的连通分量是一个相当普遍的过程。在下面的代码中,我标记了图像(识别连接组件),计算每个连接组件的像素数,然后选择像素最多的一个。

最后,从这个结果中减去它的腐蚀,我们只剩下棋盘边缘的像素(这里是蓝色覆盖在输入图像上):

我用来查找角点的技巧相当简单,但在这里失败了,因为其中一个角点不在图像中。在这四个边缘上使用 Hough 可能是一种更可靠的方法。使用this other answer 获取一些关于如何去做的想法和代码。

无论如何,我发现最靠近图像左上角的边缘像素是棋盘的左上角。其他 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')

【讨论】:

  • 感谢您的解决方案。直到侵蚀为止的一切都完美无缺。我只是在尝试使用霍夫线解决方案来抓住角落this answer。当我开始工作时我会更新
【解决方案2】:

我有一些答案给你,虽然不完整,但它可能会对你有所帮助。 我使用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()

最后一步是组合任何相交的框,因为您只对边/角感兴趣,然后只获得面积最大的框。看here看看如何组合框。

我的编码来源:OpenCV 3.1 Documentation

您的图像上的结果:

正常:

倾斜:

希望这会有所帮助!

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-05-20
    • 2012-12-31
    • 2011-09-27
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多