2017-02-17 53 views
10

Mam dwa obrazy, jeden z jedynym tłem, a drugi z tłem + wykrywalnym obiektem (w moim przypadku jest to samochód). Poniżej znajdują się zdjęciaUsuń tło obrazu za pomocą opencv Python

enter image description here

Próbuję usunąć tło tak, że mam tylko samochód w obrazie wynikowym. Poniżej znajduje się kod, który z którymi staram się uzyskać pożądane rezultaty

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) 

Uzyskany obraz poprzez odjęcie dwóch obrazów jest

enter image description here

Tutaj jest problem. Oczekiwany wynikowy obraz powinien być tylko samochodem. Ponadto, jeśli przyjrzysz się głębiej dwóm zdjęciom, zobaczysz, że nie są one dokładnie takie, jakie są, kamera nieco się poruszyła, więc trochę zaniepokoiło się tłem. Moje pytanie jest takie, że z tymi dwoma obrazami mogę odjąć tło. Nie chcę teraz używać algorytmu grabCut lub backgroundSubtractorMOG, ponieważ nie wiem od razu, co dzieje się w tych algorytmach.

Co staram się zrobić, to pojawia się następujący obraz wynikowy enter image description here

Także, jeśli to możliwe, proszę kierować do mnie z ogólnym sposobem w ten sposób nie tylko w tym konkretnym przypadku, to znaczy, mam doświadczenie w jeden obraz i tło + obiekt na drugim obrazie. Jaki mógł być najlepszy sposób na zrobienie tego. Przepraszam za tak długie pytanie.

+0

Czy obrazy są identyczne na poziomie pikseli? Spróbuj, jeśli użycie progu, np. 'Foreground [foreground> 20] = 255', poprawi twój wynik. – kazemakase

+0

Czy mógłbyś prawdopodobnie dołączyć obrazy wejściowe w postaci użytecznej? –

+0

Pytanie już zawiera poprawną odpowiedź, dlaczego przydzielono Ci nagrodę @ DHShah01? – ZdaR

Odpowiedz

8

I rozwiązać problemu przy użyciu watershed algorytmu OpenCV jest. Możesz znaleźć teorię i przykłady działu wodnego here.

Najpierw wybrałem kilka punktów (znaczników), aby określić, gdzie jest obiekt, który chcę zachować, i gdzie znajduje się tło. Ten krok jest ręczny i może się bardzo różnić w zależności od obrazu. Ponadto wymaga kilku powtórzeń, dopóki nie uzyskasz pożądanego rezultatu. Sugeruję użycie narzędzia do uzyskania współrzędnych pikseli. Następnie utworzyłem pustą tablicę całkowitą zer, z rozmiarem obrazu samochodu. A potem przypisałem pewne wartości (1: tło, [255,192,128,64]: car_parts) do pikseli w miejscach znaczników.

UWAGA: Po pobraniu zdjęcia musiałem go przyciąć, aby uzyskać ten z samochodem. Po przycięciu obraz ma rozmiar 400 x 601. Może to nie być rozmiar obrazu, który masz, więc znaczniki będą wyłączone.

Potem użyłem algorytmu wodnego. Pierwsze wejście to twój obraz, a drugie wejście to obraz znacznika (zero wszędzie z wyjątkiem pozycji znacznika). Wynik pokazano na poniższym obrazku. after watershed

Ustawiam wszystkie piksele o wartości większej niż 1 do 255 (samochód), a resztę (tło) na zero. Następnie poszerzyłem otrzymany obraz o jądro 3x3, aby uniknąć utraty informacji na zarysie samochodu. Wreszcie, użyłem rozszerzonych źrenicach obraz jako maska ​​dla obrazu oryginalnego, używając funkcji cv2.bitwise_and(), a wynik polega na poniższym obrazie: final cropped image

Oto mój kod:

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() 

Jeśli masz dużo obrazów, prawdopodobnie konieczne będzie utworzenie narzędzia do graficznego oznaczania znaczników, a nawet algorytmu automatycznego znajdowania znaczników.

+0

To fajny wynik. Jakiego rodzaju algorytmu użyjesz do automatycznego znalezienia markerów? Przydałaby się każda możliwość zapytania. Twoje zdrowie. – Chogg

+0

Najprostszym sposobem jest utworzenie narzędzia adnotacji (GUI), w którym można kliknąć części obrazu, które chcesz przechwycić. Jeśli masz dużo, ale podobnych zdjęć, możesz użyć tych samych punktów znacznika, a następnie poprawić potencjalne przesunięcia. W pełni automatyczny system wymagałby jakiejś wiedzy o tym, czego szukasz, więc wybrałbym algorytm ML, który podając słowo (tj. Samochód) spróbowałby nadać markerom obiekt na obrazie. Może DL przekroczy wszystkie inne opcje ML, ponieważ potrzebujesz wielu kroków, aby to osiągnąć (np. Klasyfikacja obrazu, identyfikacja markera itp.). – TasosGlrs

3

Problem polega na tym, że odejmujesz tablice unsigned 8-bitowe liczby całkowite. Ta operacja może się przepełnić.

Aby zademonstrować

>>> 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) 

Ponieważ używasz OpenCV, najprostszy sposób, aby osiągnąć swój cel jest użycie cv2.absdiff().

>>> cv2.absdiff(a,b) 
array([[1, 1]], dtype=uint8)