Python opencv, обнаружение на основе цвета/формы

Я пытаюсь обнаружить некоторые объекты на картинке по их форме и цвету.

Это исходное изображение, и я хочу найти две розовые кружки (выделены зеленым)

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

Я использую цветовую маску, чтобы изолировать две кружки, и результат довольно хороший, как вы можете видеть здесь:

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

Проблема в том, что могут быть обнаружены и другие объекты с похожими цветами, например, красный стул в нижней правой части.

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

Чтобы сделать все немного более надежным, я пытался дополнительно выбрать кружки, используя их форму с cv2. приблизительноPolyDP, но мне часто не удается отделить кружку от шумных областей. Форма кружки, идентифицируемая по цветовой маске, часто бывает не очень точной, поэтому аппроксимирующий многоугольник может состоять из до 10 сегментов, что делает бесполезным его отделение от шума.

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

import cv2
import numpy as np 


def main():
    cap = cv2.VideoCapture(0)
    cap.set(cv2.CAP_PROP_BUFFERSIZE, 1)

    cv2.namedWindow("Color selection")
    cv2.createTrackbar("Low_H", "Color selection", 114, 255, nothing)
    cv2.createTrackbar("Low_S", "Color selection", 76, 255, nothing)
    cv2.createTrackbar("Low_V", "Color selection", 145, 255, nothing)
    cv2.createTrackbar("Up_H", "Color selection", 170, 255, nothing)
    cv2.createTrackbar("Up_S", "Color selection", 255, 255, nothing)
    cv2.createTrackbar("Up_V", "Color selection", 255, 255, nothing)
    cv2.createTrackbar("N_erosion", "Color selection", 4, 50, nothing)
    cv2.createTrackbar("epsilon", "Color selection", 2, 20, nothing)
    cv2.createTrackbar("Area_min", "Color selection", 52, 500, nothing)
    cv2.createTrackbar("Area_max", "Color selection", 1800, 4000, nothing)


    while True:
        ret, frame = cap.read()
        frame_hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
        frame_hsv_blur = cv2.GaussianBlur(frame_hsv, (7, 7), 0)

        ## parameters selection
        l_h = cv2.getTrackbarPos("Low_H", "Color selection")
        l_s = cv2.getTrackbarPos("Low_S", "Color selection")
        l_v = cv2.getTrackbarPos("Low_V", "Color selection")
        u_h = cv2.getTrackbarPos("Up_H", "Color selection")
        u_s = cv2.getTrackbarPos("Up_S", "Color selection")
        u_v = cv2.getTrackbarPos("Up_V", "Color selection")
        N_erode = cv2.getTrackbarPos("N_erosion", "Color selection")
        eps = cv2.getTrackbarPos("epsilon", "Color selection")/100
        area_min = cv2.getTrackbarPos("Area_min", "Color selection")
        area_max = cv2.getTrackbarPos("Area_max", "Color selection")
        N_erode = N_erode if N_erode>0 else 1

        lower_values = np.array([l_h, l_s, l_v])
        upper_values = np.array([u_h, u_s, u_v])
        mask = cv2.inRange(frame_hsv_blur, lower_values, upper_values)
        kernel = np.ones((N_erode,N_erode), np.uint8)
        mask = cv2.erode(mask, kernel)

        ## find contours in image based on color mask
        contours, hierarchy = cv2.findContours(mask, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
        for contour in contours:
            area = cv2.contourArea(contour)
            perimeter = cv2.arcLength(contour, True)
            approx = cv2.approxPolyDP(contour, eps*perimeter, True)
            x,y,w,h = cv2.boundingRect(contour)
            if (area_min < area < area_max) and (2<len(approx)):
                x_0 = int(x+w/2)
                y_0 = int(y+h/2)
                frame = cv2.putText(frame, str(len(approx)), (x,y), cv2.FONT_HERSHEY_SIMPLEX, 0.8, (255,0,0), thickness=3)
                frame = cv2.circle(frame, (x_0, y_0), 10, (255,255,50), -1)

        cv2.imshow("tracking", frame)
        cv2.imshow("mask", mask)


        key = cv2.waitKey(1)
        if key == ord('q'):
            break
        elif key == ord('s'):
            cv2.imwrite("saved_image.jpg", frame)

    cv2.destroyAllWindows()
    cap.release()


def nothing(x):
    pass


if __name__ == '__main__':
    main()

Итак, опять же, мой главный вопрос касается обнаружения формы. Мне было интересно, могу ли я попробовать другой подход, чтобы лучше использовать тот факт, что я ищу очень специфическую форму, возможно, используя что-то еще, кроме cv2. приблизительноPolyDP. Какие-либо предложения?


person Carlo    schedule 21.07.2020    source источник
comment
Я чувствую, что ваши cv2.inRange() цветовые границы очень узкие, что не позволяет обнаружить рожу во всей красе. Здесь мы можем пойти на компромисс, мы можем ослабить верхнюю и нижнюю границы цвета, что, очевидно, теперь будет обнаруживать больше контуров. И затем вы можете отфильтровать их по площади и приблизительному PlyDp.   -  person ZdaR    schedule 21.07.2020
comment
Спасибо! Как я уже сказал в другом комментарии, боюсь, я неправильно сформулировал свой вопрос, я исправил его сейчас. Действительно, я могу лучше выбрать параметры для выбора цвета и закрыть изображение, но меня больше всего интересовало, могу ли я сделать что-то лучше с определением формы, учитывая, что мне нужно обнаружить объект очень специфической формы. Особенно, если в комнате есть другие объекты схожего цвета, которые я хочу исключить.   -  person Carlo    schedule 22.07.2020
comment
Похоже, вам нужен SimpleBlobDetector. В этом ответе есть пример того, как его использовать.   -  person bfris    schedule 22.07.2020


Ответы (1)


Я проверил твою фотографию. Это то, что я мог сделать лучше всего за 5 минут. потренируйтесь немного больше, и лучший результат проявится.

import cv2
import numpy as np 
from google.colab.patches import cv2_imshow


img = cv2.imread("/content/JSjbB.png")
cv2_imshow(img)
img_output = img
cv2_imshow(img_output)
frame_hsv = cv2.cvtColor(img_output, cv2.COLOR_BGR2HSV)
frame_hsv_blur = cv2.GaussianBlur(frame_hsv, (7, 7), 0)
#print(frame_hsv_blur)
lower_values = np.array([130, 100,0])
upper_values = np.array([170,255, 255])
mask = cv2.inRange(frame_hsv_blur, lower_values, upper_values)
cv2_imshow(mask)
kernel = np.ones((5,5),np.uint8)
erosion = cv2.erode(mask,kernel,iterations = 3)
dilation = cv2.dilate(erosion,kernel,iterations = 3)
cv2_imshow(dilation)

Результат примерно такой:

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

person user13915628    schedule 21.07.2020
comment
Спасибо за ответ, с вашими настройками ситуация действительно улучшается. Но, вероятно, я недостаточно ясно выразился в вопросе (я его отредактировал). Я знал, что могу лучше установить параметры цветовой маски, но полагаться только на цвета не очень надежно, и это чревато ошибками... например, если я просто слегка поверну стул, освещение на нем изменится, и я снова получить шум. Итак, в основном мой главный вопрос заключается в том, могу ли я сделать что-то лучше, связанное с выбором формы, поскольку объект, который я ищу, имеет очень специфическую форму. - person Carlo; 22.07.2020