Python: контур вокруг прямоугольника на основе заданного цвета c на темном изображении (OpenCV) - PullRequest
1 голос
/ 26 марта 2020

, поскольку я пытаюсь улучшить свои навыки с помощью OpenCV в Python, я хотел бы знать, каков наилучший способ извлечения указанного c серого тона из изображения, в основном темных цветов.

Для начала я создал тестовое изображение, чтобы протестировать различные методы с OpenCV:

Допустим, я хочу извлечь определенный c цвет в этом изображении и добавить к нему границу. На данный момент я выбрал серый прямоугольник посередине с цветом (33, 33, 34 RGB), см. Следующее:

(Вот изображение без красной рамки, чтобы проверить свои идеи: https://i.stack.imgur.com/Zf8Vb.png)

example image with border

Это то, что я пробовал до сих пор, но это не совсем работает:

img = cv2.imread(path) #Read input image
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV) # Convert from BGR to HSV color space
saturation_plane = hsv[:, :, 1] # all black/white/gray pixels are zero, and colored pixels are above zero
_, thresh = cv2.threshold(saturation_plane, 8, 255, cv2.THRESH_BINARY) # Apply threshold on s
contours = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE) # draw all contours 
contours = contours[0] if len(contours) == 2 else contours[1]
result = img.copy()

for contour in contours:
   (x, y, w, h) = cv2.boundingRect(contour) # compute the bounding box for the contour
   if width is equal to the width of the rectangle i want to extract:
       draw contour

Что если размер прямоугольника не фиксирован, чтобы я не смог его обнаружить по ширине / высоте? Более того, лучше ли конвертировать изображение в оттенки серого вместо HSV? Я просто новичок в этом, и я хотел бы услышать ваш способ достижения этого.

Заранее спасибо.

1 Ответ

1 голос
/ 26 марта 2020

Если известен указанный c цвет, вы можете начать с gray = np.all(img == (34, 33, 33), 2).

Результатом является логическая матрица с True, где BGR = (34, 33, 33), и False, где это не так.
Примечание. Порядок цветов OpenCV - BGR, а не RGB.

  • Преобразование логической матрицы в uint8: gray = gray.astype(np.uint8)*255
  • Использование findContours на gray изображении.

Преобразование изображения в HSV не имеет смысла, если вы хотите найти синий прямоугольник, но не серый прямоугольник с очень конкретными c значениями RGB.

Следующий код находит контур максимального размера с цветом (33, 33, 34 RGB):

import numpy as np
import cv2

# Read input image
img = cv2.imread('rectangles.png')

# Gel all pixels in the image - where BGR = (34, 33, 33), OpenCV colors order is BGR not RGB
gray = np.all(img == (34, 33, 33), 2)  # gray is a logical matrix with True where BGR = (34, 33, 33).

# Convert logical matrix to uint8
gray = gray.astype(np.uint8)*255

# Find contours
cnts = cv2.findContours(gray, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)[-2]  # Use index [-2] to be compatible to OpenCV 3 and 4

# Get contour with maximum area
c = max(cnts, key=cv2.contourArea)

x, y, w, h = cv2.boundingRect(c)

# Draw green rectangle for testing
cv2.rectangle(img, (x, y), (x+w, y+h), (0, 255, 0), thickness = 2)

# Show result
cv2.imshow('gray', gray)
cv2.imshow('img', img)
cv2.waitKey(0)
cv2.destroyAllWindows()

Результат:

серый:
enter image description here

img:
enter image description here


Если вы не знаете специфику c цвет в основном темных цветов, вы можете найти все контуры и найти тот, у которого наименьшее значение серого:

import numpy as np
import cv2

# Read input image
img = cv2.imread('rectangles.png')

# Convert from BGR to Gray
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# Apply threshold on gray
_, thresh = cv2.threshold(gray, 8, 255, cv2.THRESH_BINARY)

# Find contours on thresh
cnts = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)[-2]  # Use index [-2] to be compatible to OpenCV 3 and 4

min_level = 255
min_c = []

#Iterate contours, and find the darkest:
for c in cnts:
    x, y, w, h = cv2.boundingRect(c)

    # Ignore contours that are very thin (like edges)
    if w > 5 and h > 5:
        level = gray[y+h//2, x+w//2]  # Get gray level of center pixel

        if level < min_level:
            # Update min_level abd min_c
            min_level = level
            min_c = c

x, y, w, h = cv2.boundingRect(min_c)

# Draw red rectangle for testing
cv2.rectangle(img, (x, y), (x+w, y+h), (0, 0, 255), thickness = 2)

# Show result
cv2.imshow('img', img)
cv2.waitKey(0)
cv2.destroyAllWindows()

Результат:
enter image description here

...