I have a following input image:
My aim is to draw contours across the red area. To do this I have following code: import cv2
# Read image
src = cv2.imread("images.jpg", cv2.IMREAD_GRAYSCALE)
# Set threshold and maxValue
thresh = 150
maxValue = 200
# Basic threshold example
th, dst = cv2.threshold(src, thresh, maxValue, cv2.THRESH_BINARY);
# Find Contours
countours,hierarchy=cv2.findContours(dst,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)
for c in countours:
rect = cv2.boundingRect(c)
if rect[2] < 10 or rect[3] < 10: continue
x,y,w,h = rect
cv2.rectangle(src,(x,y),(x+w,y+h),(255,255,255),2)
# Draw Contour
#cv2.drawContours(dst,countours,-1,(255,255,255),3)
cv2.imshow("Contour",src)
cv2.imwrite("contour.jpg",src)
cv2.waitKey(0)
I am getting following output:
My target is to remove all the rectangles which fall inside bigger rectangle and connect the bigger rectangles for example like this:
How do I do that ?
If you use cv2.RETR_EXTERNAL
instead of cv2.RETR_TREE
in findContours
, the function will only return outer contours. So it won't return contours that are inside of another contour.
To merge contours, a very easy approach is to draw the contours filled white on a black mask, and then perform a new findContours on that mask. It will return the outline of the combined contours.
To exclude small contours: you can get the size of the contour using contourArea
and compare it to a value you set. In the code below I added a trackbar so you can set the minimum value dynamically.
Result:
Note the small rectangle to the right size. It doesn't overlap, but is above the minContourSize. If you want to exclude that contour, you can increase minContourSize, but you might also start excluding contours you do want. A solution is to set a second check on the contourSize, this time on the mask. As the mask has combined contours, you can set the threshold much higher.
If you would rather merge that contour to the larger one: you can can make the contours join on the mask by drawing a filled contour and also a non filled rectangle with an outline several pixels wide. Though a more appropriate approach would be to look into Morphological Transformations, which you can apply to the mask.
Code:
import cv2
import numpy as np
# Read image
src = cv2.imread("3E3MT.jpg", cv2.IMREAD_GRAYSCALE)
# Set threshold and maxValue
thresh = 150
maxValue = 200
# set an initial minimal contour size
minContourSize = 250
# create a window (needed for use with trackbar)
cv2.namedWindow("Contour")
def setMinSize(val):
# set the minimal contour size and find/draw contours
global minContourSize
minContourSize = val
doContours()
def doContours():
# create a copy of the image (needed for use with trackbar)
res = src.copy()
# find contours - external only
countours,hierarchy=cv2.findContours(dst,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
# create an empty mask
mask = np.zeros(src.shape[:2],dtype=np.uint8)
# draw filled boundingrects if the contour is large enough
for c in countours:
if cv2.contourArea(c) > minContourSize:
x,y,w,h = cv2.boundingRect(c)
cv2.rectangle(mask,(x,y),(x+w,y+h),(255),-1)
# find the contours on the mask (with solid drawn shapes) and draw outline on input image
countours,hierarchy=cv2.findContours(mask,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
for c in countours:
cv2.drawContours(res,[c],0,(255,255,255),2)
# show image
cv2.imshow("Contour",res)
# create a trackbar to set the minContourSize - initial is set at 250,
# maximum value is currently set at 1500, you can increase it if you like
cv2.createTrackbar("minContourSize", "Contour",250,1500,setMinSize)
# Basic threshold example
th, dst = cv2.threshold(src, thresh, maxValue, cv2.THRESH_BINARY)
# Find Contours
doContours()
# waitkey to prevent program for exiting by itself
cv2.waitKey(0)
cv2.destroyAllWindows()
You can use below code as a starting point. It's not perfect but that's an opportunity for you to improved it even further.
# Read image
src = cv2.imread("demo.jpg")
gray = cv2.cvtColor(src, cv2.COLOR_BGR2GRAY)
# binary thresholding
img_thresh = cv2.threshold(gray, 150, 255, cv2.THRESH_BINARY)[1]
# Find Contours
contours,hierarchy = cv2.findContours(img_thresh,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)
mask = np.zeros(src.shape, dtype="uint8")
for c in contours:
# get the bounding rect
x, y, w, h = cv2.boundingRect(c)
if w>80 and w<100:
cv2.rectangle(mask, (x, y), (x+w-13, y+h), (255, 255, 255), -1)
elif w>100:
cv2.rectangle(mask, (x+10, y+10), (x+w, y+h), (255, 255, 255), -1)
thresh = cv2.threshold(cv2.cvtColor(mask, cv2.COLOR_BGR2GRAY), 150, 255, cv2.THRESH_BINARY_INV)[1]
thresh = np.float32(thresh)
# corner detection in the above mask(find Harris corners)
dst = cv2.cornerHarris(thresh, 5, 3, 0.04)
# thresholding for an optimal value
ret, dst = cv2.threshold(dst, 0.1*dst.max(), 255, 0)
dst = np.uint8(dst)
# find centroids
ret, labels, stats, centroids = cv2.connectedComponentsWithStats(dst)
# refines the corners detected with sub-pixel accuracy
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 100, 0.001)
corners = cv2.cornerSubPix(thresh, np.float32(centroids), (5,5), (-1,-1), criteria)
#for i in corners:
# res_corners = cv2.circle(mask, (int(i[0]), int(i[1])), 2, (255, 0, 255), 2)
# convert detected corner coordinates values from float to int
corners = np.int32(corners)
# corner coordinate values forming a horizontal line will share same y coordinate value
# corner coordinate values forming a vertical line will share same x coordinate value
# dictionaries
# dict1 is a dictionary where key is x in (x, y) coordinate
# For example - (12, 20) and (12, 40) forming a vertical line;
# dict1 contains a key 12 and its corresponding element [20, 40]
dict1 = dict()
# dict2 is a dictionary where key is y in (x, y) coordinate
# For example - (12, 20) and (40, 20) forming a horizontal line;
# dict1 contains a key 20 and its corresponding element [12, 40]
dict2 = dict()
# populate dictionary with coordinates values detected above.
# Sample data of dictionary:
# {9: [9, 332],
# 46: [499, 584],
# 75: [332, 206]}
for i in range(len(corners)):
dict1.setdefault(corners[i][0], []).append(corners[i][1])
dict2.setdefault(corners[i][1], []).append(corners[i][0])
# empty image of same shape as original image on which we draw horizontal and vertical lines using dict1 and dict2
empty = np.zeros(src.shape, dtype="uint8")
for key, value in dict1.items():
if len(value)==2:
cv2.line(empty, (key, value[0]), (key, value[1]), (255,255,255), 2)
for key, value in dict2.items():
if len(value)==2:
cv2.line(empty, (value[0], key), (value[1], key), (255,255,255), 2)
#cv2.imshow("corner detected",res_corners)
#cv2.imshow("intermediate mask",mask)
cv2.imshow("resultant mask",empty)
cv2.waitKey(0)
Output:
Figure 1: Intermediate mask
Figure 2: Corners detected using Harris Corner detection algorithm
Figure 3: Final Result
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With