3

Using opencv-python, I'm trying to find the location of dot characters in images similar to this one:

enter image description here

42 images set here can be seen found here: https://i.stack.imgur.com/4ezTX.jpg - Bulk download available

I cant create an opencv detector that can consistently find most of the dots in those type of images, whatever works on a single image (blob detector with some parameters) usually fails on other images.

Most of my tries were around using SimpleBlobDetector is it the right approach? Is opencv not the right tool for the task? (I need a ready to use tool, training a neural net is not possible at the moment)

Your help is appreciated.

nathancy
  • 42,661
  • 14
  • 115
  • 137
Aviran
  • 5,160
  • 7
  • 44
  • 76

2 Answers2

4

Instead of using SimpleBlobDetector here's a simple approach using thresholding + contour filtering:

  • Convert image to grayscale then Otsu's threshold to obtain a binary image
  • Perform morphological opening to remove small noise
  • Find contours and filter using contour area

Here's the result with some of your images. The decimal is highlighted in green

enter image description here enter image description here

enter image description here enter image description here

enter image description here enter image description here

Code

import cv2
import numpy as np

image = cv2.imread('3.jpg')
original = image.copy()
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)[1]

kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3,3))
opening = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel, iterations=2)

cnts = cv2.findContours(opening, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
for c in cnts:
    area = cv2.contourArea(c)
    print(area)
    if area < 200:
        cv2.drawContours(original, [c], -1, (36,255,12), -1)

cv2.imshow('thresh', thresh)
cv2.imshow('opening', opening)
cv2.imshow('original', original)
cv2.waitKey()

Note: This method will work well with the assumption that the images include only the numbers and no other artifacts. Alternative approaches include:

  • Hough Circle Transform already implemented as cv2.HoughCircles(). The downside is that the function has plenty of arguments and usually only works for "perfect" circles. Since your images have decimal dots that are not perfect circles, you may not get consistent results and may get false-positives.
  • Contour approximation using cv2.arcLength() and cv2.approxPolyDP() which uses the Ramer-Douglas-Peucker algorithm, also known as the split-and-merge algorithm. The idea is that a curve can be approximated by a series of short line segments. In order to do this, we compute the perimeter of the contour and then approximate the shape of the contour depending on the number of vertices. Take a look at this for an example.
nathancy
  • 42,661
  • 14
  • 115
  • 137
1

You can try with Hough-Circles:

circles = cv2.HoughCircles(img,cv2.HOUGH_GRADIENT,1,20,
                            param1=50,param2=30,minRadius=5,maxRadius=25)

Taken from here.

Documentation

Felipe Gutierrez
  • 675
  • 6
  • 17
  • Would HoughCircles also pick up the inner black rings of the numbers (assuming they were within the min/max radius spread? Or does it only work on high intensity areas. – PeptideWitch Nov 27 '19 at 22:54
  • 1
    @PeptideWitch the algorithm searches for candidates with a high degree of radial symmetry so if the font characters has ellipses they won't be picked, if the font has chars with circular inner rings, then as you say, the min/max radius should be carefully picked. – Felipe Gutierrez Nov 28 '19 at 04:45