it-swarm.com.ru

Удалить фон изображения с помощью opencv Python

У меня есть два изображения, одно с одним фоном, а другое с фоном + обнаруживаемый объект (в моем случае это автомобиль). Ниже изображения

 enter image description here

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

import numpy as np
import cv2


original_image = cv2.imread('IMG1.jpg', cv2.IMREAD_COLOR)
gray_original = cv2.cvtColor(original_image, cv2.COLOR_BGR2GRAY)
background_image = cv2.imread('IMG2.jpg', cv2.IMREAD_COLOR)
gray_background = cv2.cvtColor(background_image, cv2.COLOR_BGR2GRAY)

foreground = np.absolute(gray_original - gray_background)
foreground[foreground > 0] = 255

cv2.imshow('Original Image', foreground)
cv2.waitKey(0)

Полученное изображение путем вычитания двух изображений 

 enter image description here

Здесь проблема. Ожидаемое полученное изображение должно быть только автомобилем. Кроме того, если вы внимательно посмотрите на эти два изображения, вы увидите, что они не совсем совпадают, т. Е. Камера немного сместилась, поэтому фон был нарушен. немного. Мой вопрос заключается в том, что с этими двумя изображениями, как я могу вычесть фон. Я не хочу использовать алгоритм grabCut или backgroundSubtractorMOG прямо сейчас, потому что сейчас я не знаю, что происходит внутри этих алгоритмов. 

То, что я пытаюсь сделать, это получить следующее полученное изображение  enter image description here

Также, если возможно, пожалуйста, наведите мне общий способ сделать это не только в этом конкретном случае, то есть у меня есть фон на одном изображении и фон + объект на втором изображении. Что может быть лучшим из возможных способов сделать это. Извините за такой длинный вопрос.

14
muazfaiz

Я решил вашу проблему используя алгоритм OpenCV водораздел . Вы можете найти теорию и примеры водораздела здесь .

Сначала я выбрал несколько точек (маркеров), чтобы указать, где находится объект, который я хочу сохранить, а где фон. Этот шаг является ручным и может сильно отличаться от изображения к изображению. Также требуется некоторое повторение, пока вы не получите желаемый результат. Я предлагаю использовать инструмент для получения координат пикселей .... Затем я создал пустой целочисленный массив нулей с размером изображения машины. А затем я назначил некоторые значения (1: фон, [255,192,128,64]: car_parts) пикселям в позициях маркера.

ПРИМЕЧАНИЕ: Когда я загрузил ваше изображение, мне пришлось обрезать его, чтобы получить изображение с автомобилем. После обрезки изображение имеет размер 400х601. Возможно, это не тот размер изображения, который у вас есть, поэтому маркеры будут отключены.

После этого я использовал алгоритм водораздела. 1-й вход - это ваше изображение, а 2-й - изображение маркера (везде ноль, за исключением позиций маркера). Результат показан на изображении ниже .  after watershed

Я установил все пиксели со значением больше 1 до 255 (автомобиль), а остальные (фон) равны нулю. Затем я расширил полученное изображение ядром 3х3, чтобы не потерять информацию о контуре автомобиля. Наконец, я использовал расширенное изображение в качестве маски для исходного изображения, используя функцию cv2.bitwise_and (), и результат находится на следующем изображении:  final cropped image

Вот мой код:

import cv2
import numpy as np
import matplotlib.pyplot as plt

# Load the image
img = cv2.imread("/path/to/image.png", 3)

# Create a blank image of zeros (same dimension as img)
# It should be grayscale (1 color channel)
marker = np.zeros_like(img[:,:,0]).astype(np.int32)

# This step is manual. The goal is to find the points
# which create the result we want. I suggest using a
# tool to get the pixel coordinates.

# Dictate the background and set the markers to 1
marker[204][95] = 1
marker[240][137] = 1
marker[245][444] = 1
marker[260][427] = 1
marker[257][378] = 1
marker[217][466] = 1

# Dictate the area of interest
# I used different values for each part of the car (for visibility)
marker[235][370] = 255    # car body
marker[135][294] = 64     # rooftop
marker[190][454] = 64     # rear light
marker[167][458] = 64     # rear wing
marker[205][103] = 128    # front bumper

# rear bumper
marker[225][456] = 128
marker[224][461] = 128
marker[216][461] = 128

# front wheel
marker[225][189] = 192
marker[240][147] = 192

# rear wheel
marker[258][409] = 192
marker[257][391] = 192
marker[254][421] = 192

# Now we have set the markers, we use the watershed
# algorithm to generate a marked image
marked = cv2.watershed(img, marker)

# Plot this one. If it does what we want, proceed;
# otherwise edit your markers and repeat
plt.imshow(marked, cmap='gray')
plt.show()

# Make the background black, and what we want to keep white
marked[marked == 1] = 0
marked[marked > 1] = 255

# Use a kernel to dilate the image, to not lose any detail on the outline
# I used a kernel of 3x3 pixels
kernel = np.ones((3,3),np.uint8)
dilation = cv2.dilate(marked.astype(np.float32), kernel, iterations = 1)

# Plot again to check whether the dilation is according to our needs
# If not, repeat by using a smaller/bigger kernel, or more/less iterations
plt.imshow(dilation, cmap='gray')
plt.show()

# Now apply the mask we created on the initial image
final_img = cv2.bitwise_and(img, img, mask=dilation.astype(np.uint8))

# cv2.imread reads the image as BGR, but matplotlib uses RGB
# BGR to RGB so we can plot the image with accurate colors
b, g, r = cv2.split(final_img)
final_img = cv2.merge([r, g, b])

# Plot the final result
plt.imshow(final_img)
plt.show()

Если у вас много изображений, вам, вероятно, потребуется создать инструмент для графического аннотирования маркеров или даже алгоритм автоматического поиска маркеров.

12
TasosGlrs

Проблема в том, что вы вычитаете массивы из 8-битных целых чисел unsigned. Эта операция может переполниться.

Показывать

>>> import numpy as np
>>> a = np.array([[10,10]],dtype=np.uint8)
>>> b = np.array([[11,11]],dtype=np.uint8)
>>> a - b
array([[255, 255]], dtype=uint8)

Поскольку вы используете OpenCV, самый простой способ достичь своей цели - использовать cv2.absdiff() .

>>> cv2.absdiff(a,b)
array([[1, 1]], dtype=uint8)
4
Dan Mašek

Я рекомендую использовать алгоритм захвата OpenCV. Сначала вы рисуете несколько линий на переднем и заднем плане и продолжаете делать это, пока ваш передний план не будет достаточно отделен от фона. Это описано здесь: https://docs.opencv.org/trunk/d8/d83/tutorial_py_grabcut.html , А также в этом видео: https://www.youtube.com/ смотреть? v = kAwxLTDDAwU

0
wordsforthewise