Обнаружение маленькой точки или десятичной точки цифры с помощью OpenCV

Я следую руководству Адриана Роузброка по распознаванию цифр на RPi, поэтому никакого тессеракта или чего-то еще: https://www.pyimagesearch.com/2017/02/13/recognizing-digits-with-opencv-and-python/

Но он не распознает десятичные точки, поэтому я очень старался создать часть, которая помогла бы это сделать. Я думаю, что подошел близко, но я не уверен, что я сделал неправильно.

Это мое изображение после предварительной обработки

введите здесь описание изображения

и это то, что происходит после попытки распознавания части

введите здесь описание изображения

Как видите, я где-то что-то не так делаю. Уже пробовали настраивать param1 и param2 в houghCircles.

Еще примеры:

введите здесь описание изображения

введите здесь описание изображения

Может ли кто-нибудь указать мне, что мне делать? Я действительно потерялся здесь

================================================================

Изображения, которые я использую введите здесь описание изображения

введите здесь описание изображения

Код, который я использую

from imutils.perspective import four_point_transform
from imutils import contours
import imutils
import cv2
import numpy

DIGITS_LOOKUP = {
        # Old Library
    #(1, 1, 1, 0, 1, 1, 1): 0, # same as new 8
    (0, 0, 1, 0, 0, 1, 0): 1,
    (1, 0, 1, 1, 1, 1, 0): 2,
    (1, 0, 1, 1, 0, 1, 1): 3,
    (0, 1, 1, 1, 0, 1, 0): 4,
    (1, 1, 0, 1, 0, 1, 1): 5,
    #(1, 1, 0, 1, 1, 1, 1): 6,
    (1, 0, 1, 0, 0, 1, 0): 7,
    (1, 1, 1, 1, 1, 1, 1): 8,
    (1, 1, 1, 1, 0, 1, 1): 9,

    # New Digital Library
        (0, 0, 1, 1, 1, 0, 1): 0,
        (1, 0, 1, 0, 0, 1, 1): 2,

        (0, 0, 1, 1, 0, 1, 1): 4,
        (0, 0, 0, 0, 0, 1, 1): 4,

        (1, 1, 0, 0, 0, 1, 1): 5,
        (1, 1, 0, 1, 1, 0, 1): 5,
        (1, 0, 0, 0, 0, 1, 1): 5,

        (1, 1, 1, 0, 0, 0, 0): 7,

        (1, 1, 0, 1, 1, 1, 1): 8,
        (1, 1, 1, 0, 1, 1, 1): 8
}

image = cv2.imread("10.jpg")

image = imutils.resize(image, height=100)
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
blurred = cv2.GaussianBlur(gray, (5, 5), 0)
edged = cv2.Canny(blurred, 120, 255, 1)
cv2.imshow("1", edged)

cnts = cv2.findContours(edged.copy(), cv2.RETR_EXTERNAL,
    cv2.CHAIN_APPROX_SIMPLE)
cnts = imutils.grab_contours(cnts)
cnts = sorted(cnts, key=cv2.contourArea, reverse=True)
displayCnt = None

for c in cnts:
    peri = cv2.arcLength(c, True)
    approx = cv2.approxPolyDP(c, 0.02 * peri, True)

    if len(approx) == 4:
        displayCnt = approx
        break

warped = four_point_transform(gray, displayCnt.reshape(4, 2))
output = four_point_transform(image, displayCnt.reshape(4, 2))

thresh = cv2.threshold(warped, 0, 255,
    cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]
cv2.imshow("2", thresh)
print(thresh.shape)

circles = cv2.HoughCircles(warped, cv2.HOUGH_GRADIENT, 7, 14, param1=0.1, param2=20, minRadius=3, maxRadius=7)

# ensure at least some circles were found
if circles is not None:
    circles = numpy.round(circles[0, :]).astype("int")

    for (x, y, r) in circles:
        cv2.circle(output, (x, y), r, (0, 255, 0), 4)
        cv2.rectangle(output, (x - 5, y - 5), (x + 5, y + 5), (0, 128, 255), -1)


    # show the output image
    cv2.imshow("test", output)
    cv2.waitKey(0)

person Aesreal    schedule 05.04.2019    source источник
comment
Первое, что я заметил, это то, что HoughCircles следует применять к двоичному изображению края, а не к warped. Таким образом, вам нужно будет запустить обнаружение краев, то есть Canny, на thresh и применить HoughCircles к выходным данным. Во-вторых, HoughCircles может быть не тем, что вам нужно, потому что точка на исходном изображении квадратная, а не круглая. findContours с последующим пороговым значением размера контура будет работать лучше.   -  person Quang Hoang    schedule 05.04.2019
comment
Вам нужно обнаруживать наименьшие связанные компоненты на изображении, а не круги или другие определенные формы. Вы знаете, что в точке ровно 9 пикселей, что кажется довольно тривиальным для обнаружения.   -  person Cris Luengo    schedule 05.04.2019
comment
я использовалconnectedComponentsWithStats, но я получаю шум, пиксели, которые даже меньше десятичного числа, но не имеют значения   -  person Aesreal    schedule 05.04.2019
comment
Другим вариантом может быть использование цветового порога, а затем определение наименьшего контура с помощью findContours   -  person nathancy    schedule 05.04.2019


Ответы (2)


Поскольку десятичная дробь может быть квадратом, а не кругом, использование cv2.HoughCircles() может быть не лучшим вариантом. Кроме того, поскольку у вас может быть фоновый шум, попытка найти подключенные компоненты может дать ложноположительные результаты.

Вот метод определения десятичной дроби с помощью cv2.boundingRect() и cv2.contourArea(). Мы могли бы установить пороговые минимальные и максимальные области, чтобы он обнаруживал только десятичные дроби, но также избегал обнаружения шума.

Попытка обнаружения на изображениях

введите здесь описание изображения введите здесь описание изображения

введите здесь описание изображения введите здесь описание изображения

from imutils.perspective import four_point_transform
from imutils import contours
import imutils
import cv2
import numpy

DIGITS_LOOKUP = {
    (1, 1, 1, 0, 1, 1, 1): 0,
    (0, 0, 1, 0, 0, 1, 0): 1,
    (1, 0, 1, 1, 1, 1, 0): 2,
    (1, 0, 1, 1, 0, 1, 1): 3,
    (0, 1, 1, 1, 0, 1, 0): 4,
    (1, 1, 0, 1, 0, 1, 1): 5,
    (1, 1, 0, 1, 1, 1, 1): 6,
    (1, 0, 1, 0, 0, 1, 0): 7,
    (1, 1, 1, 1, 1, 1, 1): 8,
    (1, 1, 1, 1, 0, 1, 1): 9
}

image = cv2.imread("10.jpg")

image = imutils.resize(image, height=100)
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
blurred = cv2.GaussianBlur(gray, (5, 5), 0)
edged = cv2.Canny(blurred, 120, 255, 1)
cv2.imshow("1", edged)

cnts = cv2.findContours(edged.copy(), cv2.RETR_EXTERNAL,
    cv2.CHAIN_APPROX_SIMPLE)
cnts = imutils.grab_contours(cnts)
cnts = sorted(cnts, key=cv2.contourArea, reverse=True)
displayCnt = None

for c in cnts:
    peri = cv2.arcLength(c, True)
    approx = cv2.approxPolyDP(c, 0.02 * peri, True)

    if len(approx) == 4:
        displayCnt = approx
        break

warped = four_point_transform(gray, displayCnt.reshape(4, 2))

thresh = cv2.threshold(warped, 0, 255,
    cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]
cv2.imshow("2", thresh)

digit_cnts = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
digit_cnts = imutils.grab_contours(digit_cnts)

threshold_max_area = 25
threshold_min_area = 5
contour_image = thresh.copy()

for c in digit_cnts:
    (x,y,w,h) = cv2.boundingRect(c)
    area = cv2.contourArea(c) 
    if area < threshold_max_area and area > threshold_min_area:
        cv2.drawContours(contour_image,[c], 0, (100,5,10), 3)

cv2.imshow("detect decimal", contour_image)
cv2.waitKey(0)
person nathancy    schedule 05.04.2019
comment
Спасибо за вашу помощь! Ваше решение намного динамичнее, чем то, что у меня есть. Вы бы порекомендовали использовать boundingRect вместо HoughCircles при попытке обнаружить круги? Или это зависит от его размера - person Aesreal; 06.04.2019
comment
Трудно сказать, я думаю, что это действительно зависит от приложения. Поскольку ваши десятичные точки не являются 100% кругами, boundingrect кажется лучше, но если вы заранее знаете, есть ли у вас настоящие круги (например, в MS Paint), тогда круги могут быть лучше - person nathancy; 07.04.2019

Работал над этим, ссылаясь на это: Как удалить небольшие связанные объекты с помощью OpenCV

получил их как результаты введите здесь описание изображения введите здесь описание изображения

Но они не очень хороши или стабильны для использования. Если точки вдруг стали больше, чем были раньше (например, более 9 пикселей или размер изображения изменился), то его больше нельзя использовать, так что это не динамический ответ, а это плохо. Но я просто оставлю это здесь, если кому интересно

Код

#find all your connected components (white blobs in your image)
nb_components, dotput, stats, centroids = cv2.connectedComponentsWithStats(thresh, connectivity=8)
#connectedComponentswithStats yields every seperated component with information on each of them, such as size
#the following part is just taking out the background which is also considered a component, but most of the time we don't want that.
sizes = stats[1:, -1]; nb_components = nb_components - 1

# minimum size of particles we want to keep (number of pixels)
#here, it's a fixed value, but you can set it as you want, eg the mean of the sizes or whatever
min_size = 50

#your answer image
img2 = numpy.zeros((dotput.shape))
#for every component in the image, you keep it only if it's above min_size
#thresh[output == 5 + 1] = 0
dots = []
for i in range(0, nb_components):
    if sizes[i] < min_size:
        dots.append(centroids[i])

#print(dots)
if dots:
    dots.sort(key = lambda x: abs(x[1]-digitCenY))
    print(dots)
    pDot = -1

    for i in range(len(digitCenX)):
        if (dots[0][0] <= digitCenX[i]) and (i > 0):
            pDot = 0
            break
        elif (digitCenX[i] <= dots[0][0]) and (i != len(digitCenX)-1):
            pDot = 0
            break
        else:
            pDot = 1

    cv2.rectangle(output, (int(dots[pDot][0]), int(dots[pDot][1])), (int(dots[pDot][0]) + 3, int(dots[pDot][1]) + 3), (0, 255, 0), 1)
person Aesreal    schedule 06.04.2019