Finding Red Circles in an Image

July 14 2017

With the OpenCV setup done before, it is now possible to go and actually use OpenCV for something practical. This is ported to Clojure from the following post, so please refer to it for the original write up.

The goal is to highlight different intensity of red circles in a picture.

The java imports when working with OpenCV are quite consistent between examples, so let’s add them along with the namespace definition.

(ns opencvfun.detectcircles
  (:import
    [org.opencv.core Point Mat Size Scalar Core CvType Mat]
    [org.opencv.imgcodecs Imgcodecs]
    [org.opencv.imgproc Imgproc]))

The source image will contain a set of shapes of different colors, and we will search the shapes from that image.

(def bgr-image 
   (Imgcodecs/imread "resources/detect/circles.jpg"))

../../detect/circles.jpg

Let’s keep a copy of the original image. We will use it later to highlight the different found shapes.

(def orig-image (.clone bgr-image))

We then move on to a different color space, to make it easier to detect

; Convert input image to HSV

(def hsv-image (Mat.))
(Imgproc/cvtColor bgr-image hsv-image Imgproc/COLOR_BGR2HSV)

../../detect/hsv.png

Let’s detect the two red shapes one by one:

(def lower-red (Mat.))
(Core/inRange hsv-image (Scalar. 0.0 100.0 100.0) (Scalar. 10.0 255.0 255.0) lower-red)

../../detect/lower-red.png

(def upper-red (Mat.))
(Core/inRange hsv-image (Scalar. 160.0 100.0 100.0) (Scalar. 179.0 255.0 255.0) upper-red)

../../detect/upper-red.png

Then combined the two shapes into one:

(def red-hue-image (Mat.))
(Core/addWeighted lower-red 1.0 upper-red 1.0 0.0 red-hue-image)

Gives the two found circles in one image:

../../detect/red-hue.png

We then add a bit of gaussian blur :

(Imgproc/GaussianBlur red-hue-image red-hue-image (Size. 9 9) 2 2)

Finally, find the shapes with Hough Circles:

(def circles (Mat.))
(Imgproc/HoughCircles 
	red-hue-image 
	circles 
	Imgproc/CV_HOUGH_GRADIENT 
	1 
	(/ (.rows red-hue-image) 8) 
	100 20 0 0)

The circle mat contains location and radius information, that can now be used to highlight the different shapes:

(doseq [i (range 0 (.cols circles))]
  (let [ circle (.get circles 0 i) 
         x (nth circle 0) 
         y (nth circle 1) 
         r (nth circle 2) ]
  (Imgproc/circle 
  	orig-image 
  	(Point. x y) 
  	r 
  	(Scalar. 0 255 0) 
  	5)))

../../detect/detected.png

When noise is added to the original picture, as in below:

../../detect/circles_rectangles_noise.jpg

The recognition gets a bit confused and gives a lot of false positive:

../../detect/confused.png

This is solved by adding some extra blurring to the original image before converting it to hsv color mode. So:

; extra median blur before the 
(Imgproc/medianBlur bgr-image bgr-image 3)

; ... then continue as before ...
(Imgproc/cvtColor bgr-image hsv-image Imgproc/COLOR_BGR2HSV)

The intermediate image gets slightly easier to analyse:

../../detect/confused-median-blur.png

And the finding circle step is now successful:

../../detect/confused-solved.png

Detecting the blue circle even though it would be changing the title of this blog post, is working along the same lines:

; Remove the red range finding

; (def lower-red (Mat.))
; (Core/inRange hsv-image (Scalar. 0.0 100.0 100.0) (Scalar. 10.0 255.0 255.0) lower-red)
; (def upper-red (Mat.))
; (Core/inRange hsv-image (Scalar. 160.0 100.0 100.0) (Scalar. 179.0 255.0 255.0) upper-red)
; (def red-hue-image (Mat.))
; (Core/addWeighted lower-red 1.0 upper-red 1.0 0.0 red-hue-image)

; Add the blue range finding

(def red-hue-image (Mat.))
(Core/inRange hsv-image (Scalar. 110.0 50.0 50.0) (Scalar. 130.0 255.0 255.0) red-hue-image)

../../detect/blue.png

Note, you’re not always forced to convert to HSV before looking for shapes. Drawing your own blue Rectangle:

(def mat-1 (Mat. 100 100 CvType/CV_8UC3 (Scalar. 0 0 0)))
(def blue (Scalar. 255 0 0))
(Imgproc/rectangle mat-1 (Point. 20 20) (Point. 50 50) blue Core/FILLED)

../../detect/blue-1.png

And looking for it, gives some pretty good result:

(def mat-2 (Mat.))
(Core/inRange mat-1 blue blue mat-2)

../../detect/blue-2.png

Built with Hugo

© Nicolas Modrzyk 2017 - hellonico @ gmail dot com