I am using OpenCV to process an image of a form which contains main checkboxes. I have used the pixel coordinates to crop around the checkbox so all I am left with is just the single checkbox. From there, I need to detect if there is anything inside of that checkbox (x, checkmark, slash, etc.) or not.
I used the code from this response and it was able to successfully draw a line around the checkbox, successfully detecting the checkbox (and part of the x). However, how do I then get it to tell me if the box is checked off in some way or if it's empty?
The checkbox in question before the code:
The code:
# Code courtesy of Sreekiran A R on Stack Overflow: https://stackoverflow.com/questions/63084676/checkbox-detection-opencv
image=box_5a_f
### converting BGR to Grayscale
gray_scale=cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)
### Binarising image
th1,img_bin = cv2.threshold(gray_scale,180,225,cv2.THRESH_OTSU)
### defining kernels
lWidth = 2
lineMinWidth = 15
kernal1 = np.ones((lWidth,lWidth), np.uint8)
kernal1h = np.ones((1,lWidth), np.uint8)
kernal1v = np.ones((lWidth,1), np.uint8)
kernal6 = np.ones((lineMinWidth,lineMinWidth), np.uint8)
kernal6h = np.ones((1,lineMinWidth), np.uint8)
kernal6v = np.ones((lineMinWidth,1), np.uint8)
### finding horizontal lines
img_bin_h = cv2.morphologyEx(~img_bin, cv2.MORPH_CLOSE, kernal1h) # bridge small gap in horizonntal lines
img_bin_h = cv2.morphologyEx(img_bin_h, cv2.MORPH_OPEN, kernal6h) # kep ony horiz lines by eroding everything else in hor direction
## detect vert lines
img_bin_v = cv2.morphologyEx(~img_bin, cv2.MORPH_CLOSE, kernal1v) # bridge small gap in vert lines
img_bin_v = cv2.morphologyEx(img_bin_v, cv2.MORPH_OPEN, kernal6v)# kep ony vert lines by eroding everything else in vert direction
def fix(img):
img[img>127]=255
img[img<127]=0
return img
img_bin_final = fix(fix(img_bin_h)|fix(img_bin_v))
### getting labels
ret, labels, stats,centroids = cv2.connectedComponentsWithStats(~img_bin_final, connectivity=8, ltype=cv2.CV_32S)
### drawing recangles for visualisation
for x,y,w,h,area in stats[2:]:
cv2.rectangle(image,(x,y),(x+w,y+h),(0,0,255),2)
The checkbox after the code:
I could also adjust my pixel coordinates to only crop the inside of the checkbox, but I'm still not sure what step to take from there and if it would really do anything to solve my issue.


I was having a bit of fun with this question, so my answer might not be the best. I thought that something recursive might work quite nicely here, something like find largest contour, get a smaller window, condition? If yes then break, if not then largest contour, get a smaller window, condition? and so on... until the condition is met. For my code I put the condition as the
cv2.contourArea. Feel free to change, here's the code:The summary:
And here are the images, getting smaller each time. From here, you can use the window as you wish.
Hope this helps you further!