openCV polygon detection

Viewed 527

I am supposed to write a program to process images of this type: enter image description here

(All images are of this format: green rectangles, and blue circles). The circles are representing the binary representation of the number. The task is to output the decimal number after detecting the circles.

My approach was to first find the green rectangle (I did not use colour masking, reason explained later) and obtain its width and height.

Next, I reasoned by symmetry that the centers of the circles must be at distances w/8,3w/8,5w/8 and 7w/8 from the left edge of the rectangle. (Horizontally).

So, I used HoughCircles() method, and then tried to express the x-coordinates of the centers in the form (2x-1)w/8. Clearly, the decimal equivalent of each circle is given by exp=2^(4-x).So, I used n+=2**exp to obtain the decmal representation.

I used the coordinates of the rectangle to also approximate the appropriate minRadius and maxRadius values for the HoughCircles() method, in order to not detect unnecessary circles.

import numpy as np
import cv2 as cv

img = cv.imread("7.jpeg")

img_height,img_w,c=img.shape


n=0 #stores the decimal rep.

width=0 #dimensions of the rectangle
height=0

start_x=0 #starting coordinates of the rectangle
start_y=0
end_x=0 #ending '''''''
end_y=0
     
minr=0 #for the houghCircles() method
maxr=0
mind=0

output = img.copy()
gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)


_, th = cv.threshold(gray, 240, 255, cv.THRESH_BINARY)
contours, _ = cv.findContours(th, cv.RETR_TREE, cv.CHAIN_APPROX_NONE)

for contour in contours:
    approx = cv.approxPolyDP(contour, 0.01* cv.arcLength(contour, True), True)
    
    cv.drawContours(img, [approx], 0, (0, 0, 0), 5)
    x = approx.ravel()[0]
    y = approx.ravel()[1] 

    
    if len(approx) == 4 and x>15 :
        
        
        x1 ,y1, w, h = cv.boundingRect(approx)
        aspectRatio = float(w)/h
        if aspectRatio <= 0.95 or aspectRatio >= 1.05:

          
          width=w
          height=h
          start_x=x1
          start_y=y1
          end_x=start_x+width
          end_y=start_y+height
          cv.rectangle(output, (start_x,start_y), (end_x,end_y), (0,0,255),3)
          cv.putText(output, "rectangle "+str(x)+" , " +str(y-5), (x, y-5), cv.FONT_HERSHEY_COMPLEX, 0.5, (0, 0, 0))
          

minr=int(17*width/192)
maxr=int(7*width/64)
mind=int(width//5)


print("start",start_x,start_y)
print("width",width)
print("height",height)
print("minr", minr)
print("maxr",maxr)
print("mind",mind)

gray = cv.medianBlur(gray, 5)
circles = cv.HoughCircles(gray, cv.HOUGH_GRADIENT, 1, mind,param1=50, param2=30, minRadius=minr, maxRadius=maxr)

detected_circles = np.uint16(np.around(circles))

for (x, y ,r) in detected_circles[0, :]:
    if(y>start_y and x>start_x):
        
        print("center ", x,y)
        idx= int (((x-start_x)*8)//width) 
        exp=int(4- (0.5* (idx+1)))
        n+= 2**exp
        cv.circle(output, (x, y), r, (0, 0, 0), 3)
        cv.circle(output, (x, y), 2, (0, 255, 255), 3)
print(n)
cv.imshow('output',output)
cv.waitKey(0)
cv.destroyAllWindows()

This works perfectly, for all the images of this type. However, there is a slight drawback:

The test images are in a very "nice" format: all are of fixed width and height, all are perfectly upright, all the colours for the rectangle and circle in each image are of exactly the same shade in all the images, etc.

However, we were supposed to make a code a bit more general: in order to accommodate "not so nice" images also. For example, images of this style:

enter image description here

Essentially the same format, but the background lighting + the stand not being perfectly upright makes it slightly more challenging to generalize the code, I feel. This is why I refrained from using HSV colour masking, because there wont be a set of higher and lower values that would fit all the images.

However, what I tried to do is also failing: its not able to detect the rectangle properly. I expected it to detect multiple rectangles, but its detecting rectangles at those locations, where there aren't any at all (and not at the locations where there are rectangles).

How can I tweak my code to make it a bit more general, in order to also process the second type of image properly?

0 Answers
Related