Как я могу вырезать зеленый фон с передним планом от остальной части изображения в Python? - PullRequest
0 голосов
/ 16 января 2019

Я пытаюсь вырезать несколько изображений на зеленом фоне. Центр фотографий зеленый, и я хочу вырезать остальное из картинки. Проблема в том, что я получил фотографии из видео, поэтому иногда зеленый центр больше, а иногда меньше. Моя настоящая задача - использовать K-средства на узлах, поэтому у меня есть, например, зеленый фон и две веревки, одна синяя и одна красная.

Я использую python с opencv, numpy и matplotlib.

Я уже вырезал центр, но иногда я режу слишком много, а иногда я режу слишком мало. Размер моего изображения в этом примере составляет 1920 x 1080.

Здесь узел остался, и есть еще что-то, чтобы отрезать

Здесь узел находится в центре

Вот еще один пример

Вот мой желаемый вывод из картинки 1

Пример 1, который не работает со всеми алгоритмами

Пример 2, который не работает со всеми алгоритмами

Пример 3, который не работает со всеми алгоритмами

Вот мой Код:

import numpy as np
import cv2
import matplotlib.pyplot as plt
from PIL import Image, ImageEnhance

img = cv2.imread('path')

print(img.shape)

imgRGB = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)

crop_img = imgRGB[500:500+700, 300:300+500]

plt.imshow(crop_img)
plt.show()

Ответы [ 4 ]

0 голосов
/ 19 января 2019

На основании новых изображений, которые вы добавили, я предполагаю, что вы хотите не только вырезать не зеленые части, как вы просили, но что вы хотите меньшую рамку вокруг веревок / узла. Это верно? Если нет, то вы должны загрузить видео и немного подробнее описать цель / цель обрезки, чтобы мы могли лучше вам помочь.

Предполагая, что вам нужно обрезанное изображение только с веревками, решение очень похоже на предыдущий ответ. Однако на этот раз красный и синий цвета канатов выбираются с помощью HSV. Изображение обрезается на основе полученной маски. Если вы хотите, чтобы изображение было несколько больше, чем просто веревки, вы можете добавить дополнительные поля, но не забудьте учесть / проверить край изображения.

Примечание. Приведенный ниже код работает для изображений с полностью зеленым фоном, поэтому я предлагаю вам объединить его с одним из решений, которое выбирает только зеленую область. Я проверил это для всех ваших изображений следующим образом: я взял код из другого ответа, вставил его в функцию и добавил return roi2 в конце. Этот вывод подается во вторую функцию, которая содержит код ниже. Все изображения были обработаны успешно.

Результат:

enter image description here

Код:

import numpy as np 
import cv2

# load image
img = cv2.imread("image.JPG")
# blue
lower_val_blue = (110, 0, 0)
upper_val_blue = (179,255,155)
# red
lower_val_red = (0, 0, 150)
upper_val_red = (10,255,255)
# Threshold the HSV image
mask_blue = cv2.inRange(img, lower_val_blue, upper_val_blue)
mask_red = cv2.inRange(img, lower_val_red, upper_val_red)
# combine masks
mask_total = cv2.bitwise_or(mask_blue,mask_red)

# remove noise
kernel =  np.ones((8,8),np.uint8)
mask_total = cv2.morphologyEx(mask_total, cv2.MORPH_CLOSE, kernel)

# sum each row and each volumn of the mask
sumOfCols = np.sum(mask_total, axis=0)
sumOfRows = np.sum(mask_total, axis=1)

# Find the first and last row / column that has a sum value greater than zero, 
# which means its not all black. Store the found values in variables
for i in range(len(sumOfCols)):
    if sumOfCols[i] > 0:
        x1 = i
        print('First col: ' + str(i))
        break

for i in range(len(sumOfCols)-1,-1,-1):
    if sumOfCols[i] > 0:
        x2 = i
        print('Last col: ' + str(i))
        break

for i in range(len(sumOfRows)):
    if sumOfRows[i] > 0:
        y1 = i
        print('First row: ' + str(i))
        break

for i in range(len(sumOfRows)-1,-1,-1):
    if sumOfRows[i] > 0:
        y2 = i
        print('Last row: ' + str(i))
        break

# create a new image based on the found values
roi = img[y1:y2,x1:x2]

#show image
cv2.imshow("Result", roi)
cv2.imshow("Image", img)

cv2.waitKey(0)
cv2.destroyAllWindows()
0 голосов
/ 16 января 2019

Вы можете изменить цвет на HSV.

src = cv2.imread('path')
imgRGB = cv2.cvtColor(src, cv2.COLOR_BGR2RGB)
imgHSV = cv2.cvtColor(imgRGB, cv2.COLOR_BGR2HSV)

Затем используйте inRange, чтобы найти только зеленые значения.

lower = np.array([20, 0, 0])    #Lower values of HSV range; Green have Hue value equal 120, but in opencv Hue range is smaler [0-180]
upper = np.array([100, 255, 255])  #Uppervalues of HSV range
imgRange = cv2.inRange(imgHSV, lower, upper)

Затем используйте морфологические операции, чтобы заполнить дыры после не зеленых линий

#kernels for morphology operations
kernel_noise = np.ones((3,3),np.uint8) #to delete small noises
kernel_dilate = np.ones((30,30),np.uint8)  #bigger kernel to fill holes after ropes
kernel_erode = np.ones((38,38),np.uint8)  #bigger kernel to delete pixels on edge that was add after dilate function

imgErode = cv2.erode(imgRange, kernel_noise, 1)
imgDilate = cv2.dilate(imgErode , kernel_dilate, 1)
imgErode = cv2.erode(imgDilate, kernel_erode, 1)

Надеть маску на изображение результата. Теперь вы можете легко находить углы зеленого экрана (функция findContours) или использовать в следующих шагах результат image

res = cv2.bitwise_and(imgRGB, imgRGB, mask = imgErode)  #put mask with green screen on src image
0 голосов
/ 16 января 2019

Код ниже делает то, что вы хотите. Сначала он преобразует изображение в цветовое пространство HSV, что облегчает выбор цветов. Затем делается маска, где выбираются только зеленые части. Некоторый шум удаляется, а строки и столбцы суммируются. Наконец, создается новое изображение на основе первых / последних строк / столбцов, попадающих в зеленый выбор.

Поскольку во всех приведенных примерах нужно было обрезать немного больше вершины, я добавил код для этого. Сначала я перевернул маску. Теперь вы можете использовать сумму строк / столбцов, чтобы найти строку / столбец, который полностью находится в зеленом выделении. Это сделано для вершины. На изображении ниже окна 'Roi2' является окончательным изображением.

Редактировать: обновленный код после комментария от ts.
Обновленный результат:

enter image description here

Код:

import numpy as np 
import cv2

# load image
img = cv2.imread("gr.png")
# convert to HSV
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV) 
# set lower and upper color limits
lower_val = (30, 0, 0)
upper_val = (65,255,255)
# Threshold the HSV image to get only green colors
# the mask has white where the original image has green
mask = cv2.inRange(hsv, lower_val, upper_val)
# remove noise
kernel =  np.ones((8,8),np.uint8)
mask = cv2.morphologyEx(mask, cv2.MORPH_OPEN, kernel)

# sum each row and each volumn of the image
sumOfCols = np.sum(mask, axis=0)
sumOfRows = np.sum(mask, axis=1)

# Find the first and last row / column that has a sum value greater than zero, 
# which means its not all black. Store the found values in variables
for i in range(len(sumOfCols)):
    if sumOfCols[i] > 0:
        x1 = i
        print('First col: ' + str(i))
        break

for i in range(len(sumOfCols)-1,-1,-1):
    if sumOfCols[i] > 0:
        x2 = i
        print('Last col: ' + str(i))
        break

for i in range(len(sumOfRows)):
    if sumOfRows[i] > 0:
        y1 = i
        print('First row: ' + str(i))
        break

for i in range(len(sumOfRows)-1,-1,-1):
    if sumOfRows[i] > 0:
        y2 = i
        print('Last row: ' + str(i))
        break

# create a new image based on the found values
#roi = img[y1:y2,x1:x2]

#show images
#cv2.imshow("Roi", roi)



# optional: to cut off the extra part at the top:
#invert mask, all area's not green become white
mask_inv = cv2.bitwise_not(mask)
# search the first and last column top down for a green pixel and cut off at lowest common point
for i in range(mask_inv.shape[0]):
    if mask_inv[i,0] == 0 and mask_inv[i,x2] == 0:
        y1 = i
        print('First row: ' + str(i))
        break

# create a new image based on the found values
roi2 = img[y1:y2,x1:x2]

cv2.imshow("Roi2", roi2)
cv2.imwrite("img_cropped.jpg", roi2)
cv2.waitKey(0)
cv2.destroyAllWindows()
0 голосов
/ 16 января 2019

Первый шаг заключается в извлечении зеленого канала из вашего изображения, это легко с OpenCV NumPy и будет производить изображение в градациях серого (2D NUMPY массив)

import numpy as np
import cv2
img = cv2.imread('knots.png')
imgg = img[:,:,1] #extracting green channel 

Второй шаг - использование порогового значения, что означает превращение изображения в градациях серого в двоичное (ТОЛЬКО черно-белое) изображение, для которого OpenCV имеет функцию готовности: https://docs.opencv.org/3.4.0/d7/d4d/tutorial_py_thresholding.html

imgt = cv2.threshold(imgg,127,255,cv2.THRESH_BINARY)[1]

Теперь imgt - это массив 2D numpy, состоящий исключительно из 0 с и 255 с. Теперь вы должны решить, как вы будете искать места разрезов, я предлагаю следующее:

  • верхний ряд пикселей, содержащий не менее 50% от 255 с
  • самый нижний ряд пикселей, содержащий не менее 50% от 255 с
  • крайний левый столбец пикселя, содержащий не менее 50% от 255 с
  • крайний правый столбец пикселя, содержащий не менее 50% от 255 с

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

height = img.shape[0]
width = img.shape[1]
columns = np.apply_along_axis(np.count_nonzero,0,imgt)
rows = np.apply_along_axis(np.count_nonzero,1,imgt)

Теперь столбцы и строки являются 1D-массивами с номерами, содержащими число 255s для каждого столбца / строки, зная высоту и ширину, мы можем получить 1D-массивы с bool значениями следующим образом:

columns = columns>=(height*0.5)
rows = rows>=(width*0.5)

Здесь 0.5 означает 50%, упомянутых ранее, не стесняйтесь настроить это значение в соответствии со своими потребностями Теперь пришло время найти индекс первого True и последнего True в столбцах и строках.

icolumns = np.argwhere(columns)
irows = np.argwhere(rows)
leftcut = int(min(icolumns))
rightcut = int(max(icolumns))
topcut = int(min(irows))
bottomcut = int(max(irows))

Используя argwhere, я получил массивный 1D массив индексов True с, затем нашел самый низкий и самый большой. Наконец вы можете обрезать свое изображение и сохранить его

imgout = img[topcut:bottomcut,leftcut:rightcut]
cv2.imwrite('out.png',imgout)

Есть два места, которые могут потребовать настройки:% 255 с (в моем примере 50%) и пороговое значение (127 в cv2.threshold).

РЕДАКТИРОВАТЬ: Фиксированная линия с cv2.threshold

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...