Python OpenCV contour tree hierarchy

I am trying to implement the found algorithm here in python with OpenCV. I am new to OpenCV, so naked with me.

I am trying to implement a part of an algorithm that removes irrelevant border boundaries based on the number of internal borders that they have.

  • If the current edge boundary has exactly one or two inner edge boundaries, the inner borders can be ignored
  • If the current boundary of the edge has more than two internal border boundaries, it can be ignored.

I am having trouble defining the tree structure of the paths that I extracted from the image.

My current source:

import cv2 # Load the image img = cv2.imread('test.png') cv2.copyMakeBorder(img, 50,50,50,50,cv2.BORDER_CONSTANT, img, (255,255,255)) # Split out each channel blue = cv2.split(img)[0] green = cv2.split(img)[1] red = cv2.split(img)[2] # Run canny edge detection on each channel blue_edges = cv2.Canny(blue, 1, 255) green_edges = cv2.Canny(green, 1, 255) red_edges = cv2.Canny(red, 1, 255) # Join edges back into image edges = blue_edges | green_edges | red_edges # Find the contours contours,hierarchy = cv2.findContours(edges.copy(),cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE) # For each contour, find the bounding rectangle and draw it for cnt in contours: x,y,w,h = cv2.boundingRect(cnt) cv2.rectangle(edges,(x,y),(x+w,y+h),(200,200,200),2) # Finally show the image cv2.imshow('img',edges) cv2.waitKey(0) cv2.destroyAllWindows() 

I assumed that using RETR_TREE would give me a nice nested array of paths, but that doesn't seem to be the case. How to get a tree structure of my contours?

+7
source share
1 answer

The main confusion here is probably the fact that the returned hierarchy is an array with numbers with more dimensions than necessary. Also, it looks like the Python FindContours function returns a tuple, which is a list of paths, and an NDARRAY hierarchy ...

You can get a reasonable array of hierarchy information that more closely matches C documents by simply taking the hierarchy [0]. Then there would be a suitable shape for zip, for example, with contours.

Below is an example that will draw the outermost rectangles in green and the innermost red rectangles in this image:

enter image description here

Output:

enter image description here

Note, by the way, that the wording in the OpenCV documents is a bit ambiguous, but hierarchyDataOfAContour[2] describes the children of this circuit (if it is negative, then this is the internal circuit), and hierarchyDataOfAContour[3] describes the parents of this circuit (if it is negative, it is external circuit).

Also note: I studied the implementation of the algorithm that you talked about in the OCR document, and I saw that FindContours gave me many repetitions of almost the same outlines. This will make it difficult to detect "border boxes" as described in the article. Perhaps this is due to the fact that Canny thresholds were too low (note that I played with them as described in the document), but there might be some way to reduce this effect or just look at the average deviation of the four corners of all the boxes and exclude duplicates ...

 import cv2 import numpy # Load the image img = cv2.imread("/ContourTest.PNG") # Split out each channel blue, green, red = cv2.split(img) def medianCanny(img, thresh1, thresh2): median = numpy.median(img) img = cv2.Canny(img, int(thresh1 * median), int(thresh2 * median)) return img # Run canny edge detection on each channel blue_edges = medianCanny(blue, 0.2, 0.3) green_edges = medianCanny(green, 0.2, 0.3) red_edges = medianCanny(red, 0.2, 0.3) # Join edges back into image edges = blue_edges | green_edges | red_edges # Find the contours contours,hierarchy = cv2.findContours(edges, cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE) hierarchy = hierarchy[0] # get the actual inner list of hierarchy descriptions # For each contour, find the bounding rectangle and draw it for component in zip(contours, hierarchy): currentContour = component[0] currentHierarchy = component[1] x,y,w,h = cv2.boundingRect(currentContour) if currentHierarchy[2] < 0: # these are the innermost child components cv2.rectangle(img,(x,y),(x+w,y+h),(0,0,255),3) elif currentHierarchy[3] < 0: # these are the outermost parent components cv2.rectangle(img,(x,y),(x+w,y+h),(0,255,0),3) # Finally show the image cv2.imshow('img',img) cv2.waitKey(0) cv2.destroyAllWindows() 
+9
source

All Articles