Ограничительная рамка для объектов на основе цветного питона - PullRequest
0 голосов
/ 27 апреля 2018

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

import cv2 as cv2
import os
import numpy as np


img = cv2.imread('1 (2).png')
img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY);
ret,thresh = cv2.threshold(img,127,255,0)
im2,contours,hierarchy = cv2.findContours(thresh, 1, 2)
for item in range(len(contours)):
    cnt = contours[item]
    if len(cnt)>20:
        print(len(cnt))
        M = cv2.moments(cnt)
        cx = int(M['m10']/M['m00'])
        cy = int(M['m01']/M['m00'])
        x,y,w,h = cv2.boundingRect(cnt)
        cv2.rectangle(img,(x,y),(x+w,y+h),(0,255,0),2)
        cv2.imshow('image',img)
        cv2.waitKey(0)
        cv2.destroyAllWindows()

результат - только один объект,
pic1

когда я меняю значение 127 в этой строке на 200 в этой строке ret,thresh = cv2.threshold(img,127,255,0) я получил другой объект. pic2

вот оригинальное изображение
original picture

Вопрос в том, как я могу обнаружить все объекты за один раз?

Ответы [ 2 ]

0 голосов
/ 01 мая 2018

Подход довольно прост. Мы начинаем с преобразования в HSV и захвата только канала оттенка.

image_hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
h,_,_ = cv2.split(image_hsv)

Далее мы находим доминирующие оттенки - сначала посчитаем вхождения каждого оттенка, используя numpy.bincount (мы flatten изображение канала оттенка, чтобы сделать его одномерным ):

bins = np.bincount(h.flatten())

А затем найдите, какие из них достаточно распространены, используя numpy.where:

MIN_PIXEL_CNT_PCT = (1.0/20.0)
peaks = np.where(bins > (h.size * MIN_PIXEL_CNT_PCT))[0]

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

for i, peak in enumerate(peaks):

Мы начинаем с создания маски, которая выбирает все пиксели этого оттенка (cv2.inRange, а затем извлекает соответствующие части из входного изображения BGR (cv2.bitwise_and.

mask = cv2.inRange(h, peak, peak)
blob = cv2.bitwise_and(image, image, mask=mask)

Далее мы находим контуры (cv2.findContours всех непрерывных областей этого оттенка, чтобы мы могли обработать каждую из них по отдельности

_, contours, _ = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

Теперь для каждой из идентифицированных непрерывных областей

for j, contour in enumerate(contours):

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

bbox = cv2.boundingRect(contour)
contour_mask = np.zeros_like(mask)
cv2.drawContours(contour_mask, contours, j, 255, -1)

Тогда мы можем добавить только ROI, соответствующий ограничивающей рамке

region = blob.copy()[bbox[1]:bbox[1]+bbox[3],bbox[0]:bbox[0]+bbox[2]]
region_mask = contour_mask[bbox[1]:bbox[1]+bbox[3],bbox[0]:bbox[0]+bbox[2]]
region_masked = cv2.bitwise_and(region, region, mask=region_mask)

Или визуализировать (cv2.rectangle ограничивающий прямоугольник:

result = cv2.bitwise_and(blob, blob, mask=contour_mask)
top_left, bottom_right = (bbox[0], bbox[1]), (bbox[0]+bbox[2], bbox[1]+bbox[3])
cv2.rectangle(result, top_left, bottom_right, (255, 255, 255), 2)

Или сделайте любую другую обработку, какую захотите.


Полный скрипт

import cv2
import numpy as np

# Minimum percentage of pixels of same hue to consider dominant colour
MIN_PIXEL_CNT_PCT = (1.0/20.0)

image = cv2.imread('colourblobs.png')
if image is None:
    print("Failed to load iamge.")
    exit(-1)

image_hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
# We're only interested in the hue
h,_,_ = cv2.split(image_hsv)
# Let's count the number of occurrences of each hue
bins = np.bincount(h.flatten())
# And then find the dominant hues
peaks = np.where(bins > (h.size * MIN_PIXEL_CNT_PCT))[0]

# Now let's find the shape matching each dominant hue
for i, peak in enumerate(peaks):
    # First we create a mask selecting all the pixels of this hue
    mask = cv2.inRange(h, peak, peak)
    # And use it to extract the corresponding part of the original colour image
    blob = cv2.bitwise_and(image, image, mask=mask)

    _, contours, _ = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

    for j, contour in enumerate(contours):
        bbox = cv2.boundingRect(contour)
        # Create a mask for this contour
        contour_mask = np.zeros_like(mask)
        cv2.drawContours(contour_mask, contours, j, 255, -1)

        print "Found hue %d in region %s." % (peak, bbox)
        # Extract and save the area of the contour
        region = blob.copy()[bbox[1]:bbox[1]+bbox[3],bbox[0]:bbox[0]+bbox[2]]
        region_mask = contour_mask[bbox[1]:bbox[1]+bbox[3],bbox[0]:bbox[0]+bbox[2]]
        region_masked = cv2.bitwise_and(region, region, mask=region_mask)
        file_name_section = "colourblobs-%d-hue_%03d-region_%d-section.png" % (i, peak, j)
        cv2.imwrite(file_name_section, region_masked)
        print " * wrote '%s'" % file_name_section

        # Extract the pixels belonging to this contour
        result = cv2.bitwise_and(blob, blob, mask=contour_mask)
        # And draw a bounding box
        top_left, bottom_right = (bbox[0], bbox[1]), (bbox[0]+bbox[2], bbox[1]+bbox[3])
        cv2.rectangle(result, top_left, bottom_right, (255, 255, 255), 2)
        file_name_bbox = "colourblobs-%d-hue_%03d-region_%d-bbox.png" % (i, peak, j)
        cv2.imwrite(file_name_bbox, result)
        print " * wrote '%s'" % file_name_bbox

Консольный вывод

Found hue 32 in region (186, 184, 189, 122).
 * wrote 'colourblobs-0-hue_032-region_0-section.png'
 * wrote 'colourblobs-0-hue_032-region_0-bbox.png'
Found hue 71 in region (300, 197, 1, 1).
 * wrote 'colourblobs-1-hue_071-region_0-section.png'
 * wrote 'colourblobs-1-hue_071-region_0-bbox.png'
Found hue 71 in region (301, 195, 1, 1).
 * wrote 'colourblobs-1-hue_071-region_1-section.png'
 * wrote 'colourblobs-1-hue_071-region_1-bbox.png'
Found hue 71 in region (319, 190, 1, 1).
 * wrote 'colourblobs-1-hue_071-region_2-section.png'
 * wrote 'colourblobs-1-hue_071-region_2-bbox.png'
Found hue 71 in region (323, 176, 52, 14).
 * wrote 'colourblobs-1-hue_071-region_3-section.png'
 * wrote 'colourblobs-1-hue_071-region_3-bbox.png'
Found hue 71 in region (45, 10, 330, 381).
 * wrote 'colourblobs-1-hue_071-region_4-section.png'
 * wrote 'colourblobs-1-hue_071-region_4-bbox.png'
Found hue 109 in region (0, 0, 375, 500).
 * wrote 'colourblobs-2-hue_109-region_0-section.png'
 * wrote 'colourblobs-2-hue_109-region_0-bbox.png'
Found hue 166 in region (1, 397, 252, 103).
 * wrote 'colourblobs-3-hue_166-region_0-section.png'
 * wrote 'colourblobs-3-hue_166-region_0-bbox.png'

Пример выходных изображений

Желтая ограничительная рамка:

Hue 32 bounding box

Желтая добытая область:

Hue 32 section

Самая большая зеленая ограничительная рамка (есть также несколько других небольших непересекающихся областей):

Hue 71 largest bounding box

... и соответствующий извлеченный регион:

Hue 71 largest section

0 голосов
/ 28 апреля 2018

Первый шаг - понять, что делает ваш алгоритм ... в частности, эта функция: ret,thresh = cv2.threshold(img,127,255,0)

значение 127 - это значение шкалы серого от 0 до 255. Функция порога изменяет значения пикселей ниже 127 до 0 и выше от 127 до 255

Применительно к вашему цветному изображению выходной сигнал в оттенках серого как для зеленого, так и для желтого шариков превышает 127, поэтому оба значения изменяются на 255, и, следовательно, оба фиксируются методом findContours()

Вы можете запустить imshow на thresh объекте, чтобы точно понять, что происходит.

Теперь, когда вы заменяете 127 на 200, только желтый шарик имеет значение шкалы серого больше 200, поэтому только этот шарик виден в thresh Мат

Чтобы обнаружить "все объекты" одновременно, пожалуйста, поэкспериментируйте с методом threshold и изучите объект thresh, используя imshow

...