Python opencv sorting contours

前端 未结 3 1567
梦谈多话
梦谈多话 2020-12-08 02:56

I am following this question:

How can I sort contours from left to right and top to bottom?

to sort contours from left-to-right and top-to-bottom. However, m

相关标签:
3条回答
  • 2020-12-08 03:33

    This is from Adrian Rosebrock for sorting contours based on location link:

    # import the necessary packages
    import numpy as np
    import argparse
    import imutils
    import cv2
    
    
    def sort_contours(cnts, method="left-to-right"):
        # initialize the reverse flag and sort index
        reverse = False
        i = 0
    
        # handle if we need to sort in reverse
        if method == "right-to-left" or method == "bottom-to-top":
            reverse = True
    
        # handle if we are sorting against the y-coordinate rather than
        # the x-coordinate of the bounding box
        if method == "top-to-bottom" or method == "bottom-to-top":
            i = 1
    
        # construct the list of bounding boxes and sort them from top to
        # bottom
        boundingBoxes = [cv2.boundingRect(c) for c in cnts]
        (cnts, boundingBoxes) = zip(*sorted(zip(cnts, boundingBoxes),
            key=lambda b:b[1][i], reverse=reverse))
    
        # return the list of sorted contours and bounding boxes
        return (cnts, boundingBoxes)
    
    def draw_contour(image, c, i):
        # compute the center of the contour area and draw a circle
        # representing the center
        M = cv2.moments(c)
        cX = int(M["m10"] / M["m00"])
        cY = int(M["m01"] / M["m00"])
    
        # draw the countour number on the image
        cv2.putText(image, "#{}".format(i + 1), (cX - 20, cY), cv2.FONT_HERSHEY_SIMPLEX,
            1.0, (255, 255, 255), 2)
    
        # return the image with the contour number drawn on it
        return image
    
    # construct the argument parser and parse the arguments
    ap = argparse.ArgumentParser()
    ap.add_argument("-i", "--image", required=True, help="Path to the input image")
    ap.add_argument("-m", "--method", required=True, help="Sorting method")
    args = vars(ap.parse_args())
    
    # load the image and initialize the accumulated edge image
    image = cv2.imread(args["image"])
    accumEdged = np.zeros(image.shape[:2], dtype="uint8")
    
    # loop over the blue, green, and red channels, respectively
    for chan in cv2.split(image):
        # blur the channel, extract edges from it, and accumulate the set
        # of edges for the image
        chan = cv2.medianBlur(chan, 11)
        edged = cv2.Canny(chan, 50, 200)
        accumEdged = cv2.bitwise_or(accumEdged, edged)
    
    # show the accumulated edge map
    cv2.imshow("Edge Map", accumEdged)
    
    # find contours in the accumulated image, keeping only the largest
    # ones
    cnts = cv2.findContours(accumEdged.copy(), cv2.RETR_EXTERNAL,
        cv2.CHAIN_APPROX_SIMPLE)
    cnts = imutils.grab_contours(cnts)
    cnts = sorted(cnts, key=cv2.contourArea, reverse=True)[:5]
    orig = image.copy()
    
    # loop over the (unsorted) contours and draw them
    for (i, c) in enumerate(cnts):
        orig = draw_contour(orig, c, i)
    
    # show the original, unsorted contour image
    cv2.imshow("Unsorted", orig)
    
    # sort the contours according to the provided method
    (cnts, boundingBoxes) = sort_contours(cnts, method=args["method"])
    
    # loop over the (now sorted) contours and draw them
    for (i, c) in enumerate(cnts):
        draw_contour(image, c, i)
    
    # show the output image
    cv2.imshow("Sorted", image)
    cv2.waitKey(0)
    
    0 讨论(0)
  • 2020-12-08 03:43

    What you actually need is to devise a formula to convert your contour information to a rank and use that rank to sort the contours, Since you need to sort the contours from top to Bottom and left to right so your formula must involve the origin of a given contour to calculate its rank. For example we can use this simple method:

    def get_contour_precedence(contour, cols):
        origin = cv2.boundingRect(contour)
        return origin[1] * cols + origin[0]
    

    It gives a rank to each contour depending upon the origin of contour. It varies largely when two consecutive contours lie vertically but varies marginally when contours are stacked horizontally. So in this way, First the contours would be grouped from Top to Bottom and in case of Clash the less variant value among the horizontal laid contours would be used.

    import cv2
    
    def get_contour_precedence(contour, cols):
        tolerance_factor = 10
        origin = cv2.boundingRect(contour)
        return ((origin[1] // tolerance_factor) * tolerance_factor) * cols + origin[0]
    
    img = cv2.imread("/Users/anmoluppal/Downloads/9VayB.png", 0)
    
    _, img = cv2.threshold(img, 70, 255, cv2.THRESH_BINARY)
    
    im, contours, h = cv2.findContours(img.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    
    contours.sort(key=lambda x:get_contour_precedence(x, img.shape[1]))
    
    # For debugging purposes.
    for i in xrange(len(contours)):
        img = cv2.putText(img, str(i), cv2.boundingRect(contours[i])[:2], cv2.FONT_HERSHEY_COMPLEX, 1, [125])
    

    If you see closely, the third row where 3, 4, 5, 6 contours are placed the 6 comes between 3 and 5, The reason is that the 6th contour is slightly below the line of 3, 4, 5 contours.

    Tell me is you want the output in other way around we can tweak the get_contour_precedence to get 3, 4, 5, 6 ranks of contour corrected.

    0 讨论(0)
  • 2020-12-08 03:51

    It appears the question you linked works not with the raw contours but first obtains a bounding rectangle using cv2.boundingRect. Only then does it make sense to calculate max_width and max_height. The code you posted suggests that you are trying to sort the raw contours, not bounding rectangles. If that is not the case, can you provide a more complete piece of your code, including a list of multiple contours that you are trying to sort?

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