Finding Red Circles in an Image
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"))
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)
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)
(def upper-red (Mat.))
(Core/inRange hsv-image (Scalar. 160.0 100.0 100.0) (Scalar. 179.0 255.0 255.0) upper-red)
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:
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)))
When noise is added to the original picture, as in below:
The recognition gets a bit confused and gives a lot of false positive:
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:
And the finding circle step is now successful:
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)
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)
And looking for it, gives some pretty good result:
(def mat-2 (Mat.))
(Core/inRange mat-1 blue blue mat-2)