Python: Как вырезать область с указанным c цветом из изображения (OpenCV, Numpy) - PullRequest
3 голосов
/ 20 марта 2020

, поэтому я пытался закодировать сценарий Python, который принимает изображение в качестве входных данных, а затем вырезает прямоугольник с заданным c цветом фона. Однако, что вызывает проблемы для моих навыков кодирования, это то, что прямоугольник не находится в фиксированной позиции на каждом изображении (позиция будет случайной).

Я не очень понимаю, как управлять функциями numpy. Я также читал кое-что об OpenCV, но я совершенно новичок в этом. До сих пор я просто обрезал изображения с помощью функции «.crop», но тогда мне пришлось бы использовать фиксированные значения.

Вот так могло бы выглядеть входное изображение, и теперь я хотел бы определить положение желтый прямоугольник, а затем обрезать изображение до его размера.

Помощь приветствуется, спасибо заранее.

An example of how the picture could look (initial example)

Updated image, how it really looks

Edit: @ MarkSetchell работает довольно хорошо, но обнаружил проблему для другой тестовой картинки. Проблема с другим изображением состоит в том, что в верхней и нижней части изображения есть 2 небольших пикселя одинакового цвета, что приводит к ошибкам или плохому кадрированию.

Ответы [ 3 ]

3 голосов
/ 21 марта 2020

Обновленный ответ

Я обновил свой ответ, чтобы справиться с пятнами шумных посторонних пикселей того же цвета, что и желтая рамка. Это работает, запустив медианный фильтр 3x3 поверх изображения, чтобы удалить пятна:

#!/usr/bin/env python3

import numpy as np
from PIL import Image, ImageFilter

# Open image and make into Numpy array
im = Image.open('image.png').convert('RGB')
na = np.array(im)
orig = na.copy()    # Save original

# Median filter to remove outliers
im = im.filter(ImageFilter.MedianFilter(3))

# Find X,Y coordinates of all yellow pixels
yellowY, yellowX = np.where(np.all(na==[247,213,83],axis=2))

top, bottom = yellowY[0], yellowY[-1]
left, right = yellowX[0], yellowX[-1]
print(top,bottom,left,right)

# Extract Region of Interest from unblurred original
ROI = orig[top:bottom, left:right]

Image.fromarray(ROI).save('result.png')

Исходный ответ

Хорошо, ваш желтый цвет - rgb(247,213,83), поэтому мы хотим найти координаты X, Y всех желтых пикселей:

#!/usr/bin/env python3

from PIL import Image
import numpy as np

# Open image and make into Numpy array
im = Image.open('image.png').convert('RGB')
na = np.array(im)

# Find X,Y coordinates of all yellow pixels
yellowY, yellowX = np.where(np.all(na==[247,213,83],axis=2))

# Find first and last row containing yellow pixels
top, bottom = yellowY[0], yellowY[-1]
# Find first and last column containing yellow pixels
left, right = yellowX[0], yellowX[-1]

# Extract Region of Interest
ROI=na[top:bottom, left:right]

Image.fromarray(ROI).save('result.png')

enter image description here


Вы можете сделать то же самое в Терминал с ImageMagick :

# Get trim box of yellow pixels
trim=$(magick image.png -fill black +opaque "rgb(247,213,83)" -format %@ info:)

# Check how it looks
echo $trim
251x109+101+220

# Crop image to trim box and save as "ROI.png"
magick image.png -crop "$trim" ROI.png

Если вы все еще используете ImageMagick v6 вместо v7, замените magick на convert.

2 голосов
/ 21 марта 2020

Я вижу темные и светло-серые области по бокам и сверху, белую область и желтый прямоугольник с серыми треугольниками внутри белой области.

Первым этапом, который я предлагаю, является преобразование изображения из цветового пространства RGB в HSV цветовое пространство.
Цветовой канал S в пространстве HSV - это " Канал насыщенности цвета ".
Все бесцветные (серый / черный / белый) нули, а желтые пиксели выше нулей в канале S.

Следующие этапы:

  • Применить порог на канале S (преобразовать его в двоичное изображение).
    Желтые пиксели переходят в 255, а остальные - в ноль.
  • Найти контуры в тре sh (найти только внешний контур - только прямоугольник).
  • Инвертировать полярность пикселей внутри прямоугольника.
    Серые треугольники становятся 255, и остальные пиксели - нули.
  • Найти контуры в тре sh - найти серые треугольники.

Вот код:

import numpy as np
import cv2

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

# Convert from BGR to HSV color space
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)

# Get the saturation plane - all black/white/gray pixels are zero, and colored pixels are above zero.
s = hsv[:, :, 1]

# Apply threshold on s - use automatic threshold algorithm (use THRESH_OTSU).
ret, thresh = cv2.threshold(s, 0, 255, cv2.THRESH_BINARY+cv2.THRESH_OTSU)

# Find contours in thresh (find only the outer contour - only the rectangle).
contours = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)[-2]  # [-2] indexing takes return value before last (due to OpenCV compatibility issues).

# Mark rectangle with green line
cv2.drawContours(img, contours, -1, (0, 255, 0), 2)

# Assume there is only one contour, get the bounding rectangle of the contour.
x, y, w, h = cv2.boundingRect(contours[0])

# Invert polarity of the pixels inside the rectangle (on thresh image).
thresh[y:y+h, x:x+w] = 255 - thresh[y:y+h, x:x+w]

# Find contours in thresh (find the triangles).
contours = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)[-2]  # [-2] indexing takes return value before last (due to OpenCV compatibility issues).

# Iterate triangle contours
for c in contours:
    if cv2.contourArea(c) > 4:  #  Ignore very small contours
        # Mark triangle with blue line
        cv2.drawContours(img, [c], -1, (255, 0, 0), 2)

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

S цветовой канал в цветовом пространстве HSV:
enter image description here

thresh - S после порога:
enter image description here

thresh после изменения полярности прямоугольника:
enter image description here

Результат (прямоугольник и треугольники помечены):
enter image description here


Обновление:

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

import cv2
import imutils  # https://pypi.org/project/imutils/

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

# Convert from BGR to HSV color space
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)

# Get the saturation plane - all black/white/gray pixels are zero, and colored pixels are above zero.
s = hsv[:, :, 1]

cv2.imwrite('s.png', s)

# Apply threshold on s - use automatic threshold algorithm (use THRESH_OTSU).
ret, thresh = cv2.threshold(s, 0, 255, cv2.THRESH_BINARY+cv2.THRESH_OTSU)

# Find contours
cnts = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
cnts = imutils.grab_contours(cnts) 

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

# Get bounding rectangle
x, y, w, h = cv2.boundingRect(c)

# Crop the bounding rectangle out of img
out = img[y:y+h, x:x+w, :].copy()

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

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

В opencv вы можете использовать inRange. Это в основном делает любой цвет в диапазоне, который вы указали, белым, а остальные черным. Таким образом, весь желтый будет белым.

Вот документация: https://docs.opencv.org/3.4/da/d97/tutorial_threshold_inRange.html

...