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



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.