This is a follow-up to openCV polygon detection. For the second image, I wasn't getting any rectangles detected, but that was because my threshold values weren't correct for that image.
I used otsu thresholding, and added a constraint to remove small and other irrelevant rectangles that got detected.
import numpy as np
import cv2 as cv
import math
img = cv.imread("t1.jpeg")
n=0
#rectangle parameters
width=0
height=0
start_x=0
start_y=0
end_x=0
end_y=0
#houghcircles parameters
minr=0
maxr=0
mind=0
maxarea=0
area=0
output = img.copy()
gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
ret2,th = cv.threshold(gray,0,255,cv.THRESH_BINARY+cv.THRESH_OTSU)
#rectangle detection
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]
x1 ,y1, w, h = cv.boundingRect(approx)
a=w*h
if len(approx) == 4 and x>15 :
aspectRatio = float(w)/h
if aspectRatio >= 2.5 and a>50:
print(x1,y1,w,h)
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(x1)+" , " +str(y1-5), (x1, y1-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)
cv.imshow("op1",output)
#circle detection,converting binary to decimal.
circles = cv.HoughCircles(gray, cv.HOUGH_GRADIENT, 1, mind,param1=50, param2=20, 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 and y<start_y+height and x<start_x+width):
cf= ((x-start_x)*8)/width
fp= cf-math.floor(cf)
if(fp>0.50):
idx=math.ceil(cf)
else:
idx=math.floor(cf)
exp=int(4- (0.5* (idx+1)))
n+= 2**exp
print("circle",x,y,r)
cv.circle(output, (x, y), r, (0, 0, 0), 3)
cv.circle(output, (x, y), 2, (0, 255, 255), 3)
print(n)
cv.imshow("th",th)
cv.imshow("gray",gray)
cv.imshow('output',output)
cv.waitKey(0)
cv.destroyAllWindows()
The final result was this:
Now the rectangle was detected successfully, however the circle wasn't detected properly. The detected circles are roughly of the same size as the target circle, which means the parameters for HoughCircles() are correct, although the circle wasn't detected at the correct location.
This is probably because the glare results in the circle almost disappearing in the grayscale image (which I used in the HoughCircles() method):
What can I do to use the HoughCircles() method for this image?
edit: Fixed a minor error in the code. The problem still persists, however i tested with other images, and it worked with the one where the grayscale image was decent enough:
Here is one way to extract the circle in Python/OpenCV. Convert to LAB, separate the B channel, then threshold on the dark circle.
Input:
import cv2
import numpy as np
# load images
img = cv2.imread('4_sign.jpg')
# convert to LAB
lab = cv2.cvtColor(img, cv2.COLOR_BGR2LAB)
# separate B channel
b = lab[:,:,2]
# threshold and invert
thresh = cv2.threshold(b, 105, 255, cv2.THRESH_BINARY)[1]
thresh = 255 - thresh
# apply morphology to clean it up
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (7,7))
morph = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel)
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (11,11))
morph = cv2.morphologyEx(morph, cv2.MORPH_CLOSE, kernel)
# get min enclosing circle
# numpy points are (y,x), so need to transpose
points = np.argwhere(morph.transpose()>0)
center, radius = cv2.minEnclosingCircle(points)
print('center:', center, 'radius:', radius)
# draw circle on copy of input
result = img.copy()
cx = int(round(center[0]))
cy = int(round(center[1]))
rr = int(round(radius))
cv2.circle(result, (cx,cy), rr, (255,255,255), 2)
# save output
cv2.imwrite('4_sign_circle.jpg', result)
# display results
cv2.imshow('thresh',thresh)
cv2.imshow('morph',morph)
cv2.imshow('result',result)
cv2.waitKey(0)
cv2.destroyAllWindows()
Result:
ADDITION
Here is a version using HoughCircles.
import cv2
import numpy as np
# load images
img = cv2.imread('4_sign.jpg')
ht, wd = img.shape[:2]
minhw = min(ht,wd)
# convert to LAB
lab = cv2.cvtColor(img, cv2.COLOR_BGR2LAB)
# separate B channel
b = lab[:,:,2]
# threshold and invert
thresh = cv2.threshold(b, 105, 255, cv2.THRESH_BINARY)[1]
thresh = 255 - thresh
# apply morphology to clean it up
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (7,7))
morph = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel)
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (11,11))
morph = cv2.morphologyEx(morph, cv2.MORPH_CLOSE, kernel)
# get circle from HoughCircles
min_dist = int(minhw/20)
circles = cv2.HoughCircles(morph, cv2.HOUGH_GRADIENT, 1, minDist=min_dist, param1=100, param2=10, minRadius=30, maxRadius=60)
#print(circles)
result = img.copy()
for circle in circles[0]:
# draw the circle on copy of input
(x,y,r) = circle
center = (x,y)
radius = r
print('center:', center, 'radius:', radius)
x = int(x)
y = int(y)
cv2.circle(result, center, radius, (255, 255, 255), 2)
# save output
cv2.imwrite('4_sign_circle2.jpg', result)
# display results
cv2.imshow('thresh',thresh)
cv2.imshow('morph',morph)
cv2.imshow('result',result)
cv2.waitKey(0)
Result:
Textual Information:
center: (410.5, 686.5) radius: 54.4