2

So I'm trying to find every square on a image, but I'm getting too many detections. I only want to detect the big cubes, and not all the noise around. My problem is also that the cubes can be different colours, but they will always be the same size.

I've written some code, but it doesn't work.

import numpy as np 
import cv2 
 
img = cv2.imread('gulRec.jpg') 
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) 

 
imagem = (255-gray)
ret,thresh = cv2.threshold(imagem,120,200,1) 
 
contours,h = cv2.findContours(thresh,1,2)

for cnt in contours: 
    approx = cv2.approxPolyDP(cnt,0.01*cv2.arcLength(cnt,True),True) 
    print (len(approx))
    if len(approx)==4: 
        cv2.drawContours(img,[cnt],0,(0,0,255),-1) 
        
cv2.imshow('thresh',thresh)  
cv2.imshow('img',img) 
cv2.waitKey(0) 
cv2.destroyAllWindows() 

picture 1 åicture 2

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
Mads
  • 61
  • 1
  • 8
  • have you tried other method such as adaptive thresholding or pyramid based segmentation? – Dr Yuan Shenghai Nov 23 '20 at 14:10
  • Nope? what are those? – Mads Nov 23 '20 at 14:38
  • try some Canny edge and shi-tomasi (goodFeaturesToTrack() if I remember correctly) – Daemon Painter Nov 23 '20 at 15:01
  • You can use [contour features](https://docs.opencv.org/3.4/dd/d49/tutorial_py_contour_features.html) in your method. Like choosing a threshold for the area. – Burak Nov 23 '20 at 15:12
  • But before that, you need to have a better binary image. Canny is a good option. Also, have a look at [morphological operations](https://www.mathworks.com/help/images/morphological-dilation-and-erosion.html), especially opening. – Burak Nov 23 '20 at 15:15
  • Use cv2.inRange() to threshold on yellow, then separately on red. Then use morphology to clean each. Then combine the thresholds. Then get the contours from the thresholded image. Filter the contours by area to get only the large ones. – fmw42 Nov 23 '20 at 19:30

1 Answers1

3

Here is one way to detect the red and yellow cubes in Python/OpenCV. The basic idea for red and yellow cubes is to recognize that the hue for red and yellow are the lowest of all colors. So one can convert to HSV and use cv2.inRange() to threshold on red and yellow.

Input:

enter image description here

import cv2
import numpy as np

img = cv2.imread("4cubes.jpg")

# convert to HSV, since red and yellow are the lowest hue colors and come before green
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)

# create a binary thresholded image on hue between red and yellow
lower = (0,240,160)
upper = (30,255,255)
thresh = cv2.inRange(hsv, lower, upper)

# apply morphology
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (9,9))
clean = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel)
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (15,15))
clean = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, kernel)

# get external contours
contours = cv2.findContours(clean, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
contours = contours[0] if len(contours) == 2 else contours[1]

result1 = img.copy()
result2 = img.copy()
for c in contours:
    cv2.drawContours(result1,[c],0,(0,0,0),2)
    # get rotated rectangle from contour
    rot_rect = cv2.minAreaRect(c)
    box = cv2.boxPoints(rot_rect)
    box = np.int0(box)
    # draw rotated rectangle on copy of img
    cv2.drawContours(result2,[box],0,(0,0,0),2)

# save result
cv2.imwrite("4cubes_thresh.jpg",thresh)
cv2.imwrite("4cubes_clean.jpg",clean)
cv2.imwrite("4cubes_result1.png",result1)
cv2.imwrite("4cubes_result2.png",result2)

# display result
cv2.imshow("thresh", thresh)
cv2.imshow("clean", clean)
cv2.imshow("result1", result1)
cv2.imshow("result2", result2)
cv2.waitKey(0)
cv2.destroyAllWindows()

Threshold image:

enter image description here

Morphology cleaned image:

enter image description here

Contours:

enter image description here

Rotated bounding boxes:

enter image description here

fmw42
  • 46,825
  • 10
  • 62
  • 80