A_N
A_N

Reputation: 127

Python OpenCV - Trying to identify a completely visible tile (all four edges are visible) and draw a green contour edges

I am working on a project to click images of the tiles and using OpenCV to to find out that the tile is visit-able or not.

Accessible tile should be shown as below -

a green contour is shown on visit-able tile

In the image below tile is not visit-able.

enter image description here

Code i have written is identifying multiple contours.

import numpy as np
import cv2

image1  = cv2.imread('floor3.jpg')#, cv2.IMREAD_ANYCOLOR | cv2.IMREAD_ANYDEPTH)
orig = image1.copy()
cv2.imshow("Game Boy Screen", orig)
cv2.waitKey(0)

gray = cv2.cvtColor(image1, cv2.COLOR_BGR2GRAY)
gray = cv2.bilateralFilter(gray, 11, 17, 17)
edged = cv2.Canny(gray, 30, 200)
flag, thresh = cv2.threshold(edged, 120, 255, cv2.THRESH_BINARY)
im2, contours, hierarchy = cv2.findContours(thresh,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)

try: hierarchy = hierarchy[0]
except: hierarchy = []

height, width, _ = image1.shape
min_x, min_y = width, height
max_x = max_y = 0

# computes the bounding box for the contour, and draws it on the frame,
for contour, hier in zip(contours, hierarchy):
    (x,y,w,h) = cv2.boundingRect(contour)
    min_x, max_x = min(x, min_x), max(x+w, max_x)
    min_y, max_y = min(y, min_y), max(y+h, max_y)
    if w > 80 and h > 80:
        cv2.rectangle(orig, (x,y), (x+w,y+h), (255, 0, 0), 2)

if max_x - min_x > 0 and max_y - min_y > 0:
    cv2.rectangle(orig, (min_x, min_y), (max_x, max_y), (255, 0, 0), 3)

cv2.imshow("Game Boy Screen", orig)
cv2.waitKey(0)

Below is the output i am getting -

Instead of 1 contour as shown in first image i am getting multiple contours

Original image:

enter image description here

Upvotes: 0

Views: 2073

Answers (1)

Kinght 金
Kinght 金

Reputation: 18341

Original:

  • First, you should also put your original image.
  • Second, using minAreaRect other than boundingRect to find the rotated rectangles.

Update:

I don't think do findContours on Canny edges is a good idea. I just do it on threshed binary image, then remove the contours with small area. So we can get the boundary of regions.Then how to judge whether the region is complete or not, it's upto your math knowledge.

enter image description here


#!/usr/bin/python3
# 2017.12.09 00:25:47 CST
# 2017.12.09 14:26:01 CST
import cv2
import numpy as np

img = cv2.imread("test.jpg")
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
gray = cv2.bilateralFilter(gray, 11, 17, 17)
th, threshed = cv2.threshold(gray, 200, 255, cv2.THRESH_BINARY)
cnts = cv2.findContours(threshed,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)[-2]

canvas = np.zeros_like(img, np.uint8)
H,W = img.shape[:2]
AREA = H*W
for cnt in cnts:
    area = cv2.contourArea(cnt)
    if(area<AREA/100):
        continue
    _ = cv2.drawContours(canvas, [cnt], -1, (0,255,0), 1, cv2.LINE_AA)


cv2.imwrite("result.png", canvas)

Update 2: After get the contours of regions, then how to judge whether it's complete or not?

You should define what is complete? In this situation, the complete tile means it is a quadrilateral, four corner points, almost equal lengths of side. Then you program the condition to tell the computer how to judge. It's your job, you should try to solve it by yourself first. enter image description here

Total code and the result:

#!/usr/bin/python3
# 2017.12.09 00:25:47 CST
# 2017.12.09 14:26:01 CST
# 2017.12.09 17:52:17 CST
import cv2
import numpy as np

## lambda: calc distance
dist = lambda pt1, pt2: ((pt1[0]-pt2[0])**2 + (pt1[1]-pt2[1])**2)**0.5

img = cv2.imread("img04.jpg")

## (1) filter, threshed
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
gray = cv2.bilateralFilter(gray, 11, 17, 17)
th, threshed = cv2.threshold(gray, 200, 255, cv2.THRESH_BINARY)

## (2) findContours
cnts = cv2.findContours(threshed,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)[-2]


## (3) some variables
H,W = img.shape[:2]
AREA = H*W
xcnts = []
R = 0.8

## (4) judge wether the contour is belongs to a complete tile
for cnt in cnts:
    ## (4.1) filter by area
    area = cv2.contourArea(cnt)
    if(area<AREA/100):
        continue
    ## (4.2) calc arpprox contour
    arclen = cv2.arcLength(cnt, closed=True)
    approx = cv2.approxPolyDP(cnt, arclen*0.02, closed=True)
    ## (4.3) filter by "complete ruler"
    ## "the complete tile means it is a quadrilateral, four corner points, almost equal lengths of side"
    pts = np.array(approx).reshape(-1,2)
    if len(pts) == 4:
        lens = np.array(list(dist(pts[i], pts[(i+1)%4]) for i in range(4)))
        flag = True
        for x in lens:
            if not (R< x/lens[0] < 1.0/R):
                flag = False
                continue
        if flag:
            xcnts.append(cnt)

## (5) draw and save
res = img.copy()
for cnt in xcnts:
    cv2.drawContours(res, [cnt], -1, (0,255,0), -1, cv2.LINE_AA)
cv2.imwrite("00result.png", res)

enter image description here


Update 3 (The last update):

I use the fixed threshed value in the my code just because your image contains a large dark origin. As for this image

enter image description here,

there is no large dark, so change the threshold flag to cv2.THRESH_OTSU, and modify the area thresh to AREA/20 or larger, modify the R smaller such as R=0.7.

Then I get the result:

enter image description here


Similar questions:

(1) How to find the object on the noisy background?

(2) Python OpenCV - Trying to identify a completely visible tile (all four edges are visible) and draw a green contour edges

Upvotes: 2

Related Questions