Algorithm to detect corners of paper sheet in photo

后端 未结 8 1477
暗喜
暗喜 2020-12-12 08:39

What is the best way to detect the corners of an invoice/receipt/sheet-of-paper in a photo? This is to be used for subsequent perspective correction, before OCR.

My

相关标签:
8条回答
  • 2020-12-12 09:22

    After edge-detection, use Hough Transform. Then, put those points in an SVM(supporting vector machine) with their labels, if the examples have smooth lines on them, SVM will not have any difficulty to divide the necessary parts of the example and other parts. My advice on SVM, put a parameter like connectivity and length. That is, if points are connected and long, they are likely to be a line of the receipt. Then, you can eliminate all of the other points.

    0 讨论(0)
  • 2020-12-12 09:24

    Also you can use MSER (Maximally stable extremal regions) over Sobel operator result to find the stable regions of the image. For each region returned by MSER you can apply convex hull and poly approximation to obtain some like this:

    But this kind of detection is useful for live detection more than a single picture that not always return the best result.

    0 讨论(0)
  • 2020-12-12 09:27

    Instead of starting from edge detection you could use Corner detection.

    Marvin Framework provides an implementation of Moravec algorithm for this purpose. You could find the corners of the papers as a starting point. Below the output of Moravec's algorithm:

    enter image description here

    0 讨论(0)
  • 2020-12-12 09:29
    1. Convert to lab space

    2. Use kmeans segment 2 cluster

    3. Then use contours or hough on one of the clusters (intenral)
    0 讨论(0)
  • 2020-12-12 09:32

    I'm Martin's friend who was working on this earlier this year. This was my first ever coding project, and kinda ended in a bit of a rush, so the code needs some errr...decoding... I'll give a few tips from what I've seen you doing already, and then sort my code on my day off tomorrow.

    First tip, OpenCV and python are awesome, move to them as soon as possible. :D

    Instead of removing small objects and or noise, lower the canny restraints, so it accepts more edges, and then find the largest closed contour (in OpenCV use findcontour() with some simple parameters, I think I used CV_RETR_LIST). might still struggle when it's on a white piece of paper, but was definitely providing best results.

    For the Houghline2() Transform, try with the CV_HOUGH_STANDARD as opposed to the CV_HOUGH_PROBABILISTIC, it'll give rho and theta values, defining the line in polar coordinates, and then you can group the lines within a certain tolerance to those.

    My grouping worked as a look up table, for each line outputted from the hough transform it would give a rho and theta pair. If these values were within, say 5% of a pair of values in the table, they were discarded, if they were outside that 5%, a new entry was added to the table.

    You can then do analysis of parallel lines or distance between lines much more easily.

    Hope this helps.

    0 讨论(0)
  • 2020-12-12 09:33

    Here's what I came up with after a bit of experimentation:

    import cv, cv2, numpy as np
    import sys
    
    def get_new(old):
        new = np.ones(old.shape, np.uint8)
        cv2.bitwise_not(new,new)
        return new
    
    if __name__ == '__main__':
        orig = cv2.imread(sys.argv[1])
    
        # these constants are carefully picked
        MORPH = 9
        CANNY = 84
        HOUGH = 25
    
        img = cv2.cvtColor(orig, cv2.COLOR_BGR2GRAY)
        cv2.GaussianBlur(img, (3,3), 0, img)
    
    
        # this is to recognize white on white
        kernel = cv2.getStructuringElement(cv2.MORPH_RECT,(MORPH,MORPH))
        dilated = cv2.dilate(img, kernel)
    
        edges = cv2.Canny(dilated, 0, CANNY, apertureSize=3)
    
        lines = cv2.HoughLinesP(edges, 1,  3.14/180, HOUGH)
        for line in lines[0]:
             cv2.line(edges, (line[0], line[1]), (line[2], line[3]),
                             (255,0,0), 2, 8)
    
        # finding contours
        contours, _ = cv2.findContours(edges.copy(), cv.CV_RETR_EXTERNAL,
                                       cv.CV_CHAIN_APPROX_TC89_KCOS)
        contours = filter(lambda cont: cv2.arcLength(cont, False) > 100, contours)
        contours = filter(lambda cont: cv2.contourArea(cont) > 10000, contours)
    
        # simplify contours down to polygons
        rects = []
        for cont in contours:
            rect = cv2.approxPolyDP(cont, 40, True).copy().reshape(-1, 2)
            rects.append(rect)
    
        # that's basically it
        cv2.drawContours(orig, rects,-1,(0,255,0),1)
    
        # show only contours
        new = get_new(img)
        cv2.drawContours(new, rects,-1,(0,255,0),1)
        cv2.GaussianBlur(new, (9,9), 0, new)
        new = cv2.Canny(new, 0, CANNY, apertureSize=3)
    
        cv2.namedWindow('result', cv2.WINDOW_NORMAL)
        cv2.imshow('result', orig)
        cv2.waitKey(0)
        cv2.imshow('result', dilated)
        cv2.waitKey(0)
        cv2.imshow('result', edges)
        cv2.waitKey(0)
        cv2.imshow('result', new)
        cv2.waitKey(0)
    
        cv2.destroyAllWindows()
    

    Not perfect, but at least works for all samples:

    1 2 3 4

    0 讨论(0)
提交回复
热议问题