I am trying to remove a fixed background from an image with a single free-falling object. The image has a single free falling object and it has a white background with a circular patch in the middle.
Below is my code for the above task. I have used OpenCV BackgroundSubtractorKNN and BackgroundSubtractorMOG2 algorithm to achieve this task. The left images should be given as input and the code should produce the right images as output.
import numpy as np
import cv2
import sys
import os
#backgroundSubtractor = cv2.createBackgroundSubtractorMOG2()
backgroundSubtractor = cv2.createBackgroundSubtractorKNN()
# apply the algorithm for background images using learning rate > 0
for i in range(1, 16):
bgImageFile = "background/BG.png"
print("Opening background", bgImageFile)
bg = cv2.imread(bgImageFile)
backgroundSubtractor.apply(bg, learningRate=0.5)
# apply the algorithm for detection image using learning rate 0
dirc = os.getcwd()
filepath = os.path.join(dirc,'data')
if not os.path.exists('saved_bgRemoved'):
os.makedirs('saved_bgRemoved')
for file in os.listdir(filepath):
stillFrame = cv2.imread(os.path.join(filepath,file))
fgmask = backgroundSubtractor.apply(stillFrame, learningRate=0)
bgImg = cv2.bitwise_and(stillFrame,stillFrame,mask=fgmask)
# show both images
cv2.imshow("original", stillFrame)
cv2.imshow("mask", fgmask)
cv2.imshow("Cut Image", bgImg)
cv2.waitKey()
cv2.destroyAllWindows()
cv2.imwrite(os.path.join('saved_bgRemoved',file), bgImg)
My code works very well with the above dataset, but it fails to work with the image data below:
It also doesn't work if the object is colored in greyish texture. I think it works well when the pixel distribution of the object is uniform and different from the background (i.e. the circular patch).
Is there any other best way to achieve this task, so that it can subtract the background even from the hollow area of the object, without subtracting parts of the object?
use below code, I think it now works
import cv2, os
def remove_bg(bg_path,im_path):
bg = cv2.imread(bg_path)
im = cv2.imread(im_path)
row,col,_ = im.shape
for i in range(0,row):
for j in range(0,col):
if ( bg[i][j][0] == im[i][j][0] and bg[i][j][1] == im[i][j][1] and bg[i][j][2] == im[i][j][2] ):
im[i][j] = [0,0,0] #it will replace background with black color, you can change it for example to [255,0,0] to replace it with red
return(im)
directory,_=os.path.split(__file__)
bg_path = directory + "\\background.png"
im_path = directory + "\\data6.png"
result = remove_bg(bg_path,im_path)
cv2.imshow("result", result)
cv2.waitKey()
cv2.imwrite(directory + "\\Result.png", result)
Related
So guys I'll explain quickly.
I have a fixed camera and I took a photo thus obtaining the "background".
Then my friend stood in front of the camera and I took another photo.
I want to get an image where there is only my friend in the foreground and where it is necessary to delete the background.
I have tried many methods (absdiff(), tensorflow + bodypix and more) but the only method that is giving me good results is using SubtractorKNN
import numpy as np
import cv2
import sys
backgroundSubtractor = cv2.createBackgroundSubtractorKNN(detectShadows=True)
# apply the algorithm for background images using learning rate > 0
for i in range(1, 16):
bgImageFile = "background.jpg"
print ("Opening background", bgImageFile)
bg = cv2.imread(bgImageFile)
backgroundSubtractor.apply(bg, learningRate=0.9)
# apply the algorithm for detection image using learning rate 0
stillFrame = cv2.imread("background-with-friend.jpg")
fgmask = backgroundSubtractor.apply(stillFrame, learningRate=0.9)
kernel = np.ones((3,3),np.uint8)
morphology_img = cv2.morphologyEx(fgmask, cv2.MORPH_OPEN,kernel,iterations=1)
nuovo = morphology_img
#'nuovo.jpg' is nuovo
ok= cv2.imread('nuovo.jpg')
giona = cv2.cvtColor(ok, cv2.COLOR_BGR2GRAY)
ret,range = cv2.threshold(giona,250,255,cv2.THRESH_BINARY)
cv2.imshow("nuovo kernel", cv2.resize(nuovo, (0, 0), fx=0.5, fy=0.5))
cv2.imshow("range", cv2.resize(range, (0, 0), fx=0.5, fy=0.5))
THE QUESTION IS:
there is a way to reconstruct the outline (e.g. left leg, face, arms), fill inside (then eliminate the black spots) and eliminate the white dots in the background (I have already used cv2.morphologyEx but use a kernel bigger would have further ruined the outline of the person).
Is possible ?
If I can get the figure of the person then I can remove the background from the original image.
EDIT
I used cv2.createBackgroundSubtractorKNN, then cv2.morphologyExand finally cv2.threshold(img,250,255,cv2.THRESH_BINARY)to delete shadows
I am trying to do some simple drawings. I wanted to use opencv (cv2) because on a second project I have to display a small animation (rectangle, size depending on a variable; updated every X seconds). However, I do not have experience with image processing libraries and opencv.
I am running into a lot of problems, one of which is that I do not know how to display/close images. The image I am creating is a simple fixation cross, black; on a light gray background:
import numpy as np
import cv2
screen_width = 1024
screen_height = 768
img = np.zeros((screen_height, screen_width, 3), np.uint8) # Black image
img = img + 210 # light gray
screen_center = (screen_width//2, screen_height//2)
rect_width = int(0.2*screen_width)
rect_height = int(0.02*screen_height)
xP1 = screen_center[0] - rect_width//2
yP1 = screen_center[1] + rect_height//2
xP2 = screen_center[0] + rect_width//2
yP2 = screen_center[1] - rect_height//2
cv2.rectangle(img, (xP1, yP1), (xP2, yP2), (0, 0, 0), -1)
xP1 = screen_center[0] - rect_height//2
yP1 = screen_center[1] + rect_width//2
xP2 = screen_center[0] + rect_height//2
yP2 = screen_center[1] - rect_width//2
cv2.rectangle(img, (xP1, yP1), (xP2, yP2), (0, 0, 0), -1)
N.B: If there is a better way to create it, I am also interested :)
My goal is for this first project to do have the following code structure:
img = load_saved_img() # The created fixation cross
display_image()
add_text_to_image('texte to add')
# do stuff
# for several minutes
while something:
do_this()
remove_text_from_image() # Alternatively, go back to the initial image/change the image
# do stuff
# for several minutes
while something:
do_this()
close_image()
I know I can add text with cv2.putText() and that I can this way create a second image with the text. What I do not know is how can I manage the displaying of the different images; especially in a light-weight fashion while "doing stuff" on the background. Most people seems to use cv2.waitKey() which is not suited since I do not want to have any user input and since it seems to be something similar to a time.sleep() during which the program is basically paused.
Any tips welcome, even on other libraries and implementation :)
As proposed by #Miki, the combination of .imshow() and .waitKey(1) is working.
cv2.imshow(window, img)
cv2.waitKey(1)
However, those can not be used with time.sleep() to pause the program. Sometimes, the display will not be updated. For instance, on a 3 second countdown:
import time
import cv2
window = 'Name of the window'
def countdown(window, images):
"""
images = [image3, image2, image1]
"""
for img in images:
cv2.imshow(window, img)
cv2.waitKey(1)
time.sleep(1)
Sometimes one of the displays will be skipped. Instead, changing the parameter of cv2.waitKey() to 1000 (timer needed) and removing the use of the time module works best, if no keyboard input is expected during this time.
I tried the below code, it doesn't show any error and runs properly, but changing the value of the alpha channel, doesn't show any change in image
img3 = cv2.cvtColor(img2, cv2.COLOR_BGR2BGRA)
img3[:,:,3] = 100
cv2.imshow('img1',img2)
cv2.imshow('img',img3)
cv2.waitKey(0)
works ok, but the output of both images are same and there is no seen-able change after applying alpha channel
i have already tried the below code
Your code is actually correct.
The simple answer is that OpenCV's imshow() ignores transparency, so if you want to see its effect, save your image as a PNG/TIFF (both of which support transparency) and view it with a different viewer - such as GIMP, Photoshop or feh.
As an alternative, I made a wrapper/decorator for OpenCV's imshow() that displays images with transparency overlaid on a chessboard like Photoshop does. So, starting with this RGBA Paddington image and this grey+alpha Paddington image:
#!/usr/bin/env python3
import cv2
import numpy as np
def imshow(title,im):
"""Decorator for OpenCV "imshow()" to handle images with transparency"""
# Check we got np.uint8, 2-channel (grey + alpha) or 4-channel RGBA image
if (im.dtype == np.uint8) and (len(im.shape)==3) and (im.shape[2] in set([2,4])):
# Pick up the alpha channel and delete from original
alpha = im[...,-1]/255.0
im = np.delete(im, -1, -1)
# Promote greyscale image to RGB to make coding simpler
if len(im.shape) == 2:
im = np.stack((im,im,im))
h, w, _ = im.shape
# Make a checkerboard background image same size, dark squares are grey(102), light squares are grey(152)
f = lambda i, j: 102 + 50*((i+j)%2)
bg = np.fromfunction(np.vectorize(f), (16,16)).astype(np.uint8)
# Resize to square same length as longer side (so squares stay square), then trim
if h>w:
longer = h
else:
longer = w
bg = cv2.resize(bg, (longer,longer), interpolation=cv2.INTER_NEAREST)
# Trim to correct size
bg = bg[:h,:w]
# Blend, using result = alpha*overlay + (1-alpha)*background
im = (alpha[...,None] * im + (1.0-alpha[...,None])*bg[...,None]).astype(np.uint8)
cv2.imshow(title,im)
if __name__ == "__main__":
# Open RGBA image
im = cv2.imread('paddington.png',cv2.IMREAD_UNCHANGED)
imshow("Paddington (RGBA)",im)
key = cv2.waitKey(0)
cv2.destroyAllWindows()
# Open Grey + alpha image
im = cv2.imread('paddington-ga.png',cv2.IMREAD_UNCHANGED)
imshow("Paddington (grey + alpha)",im)
key = cv2.waitKey(0)
cv2.destroyAllWindows()
And you will get this:
and this:
Keywords: Image, image processing, Python, alpha channel, transparency, overlay, checkerboard, chessboard, blend, blending. OpenCV, imshow, cv2.imshow.
I was trying to detect multiple colors simultaneously in an video, So I made a code including trackbar to adjust hsv values. Here is the code:
import numpy as np
import cv2
def nothing(x):
pass
cap = cv2.VideoCapture(0)
cv2.namedWindow('image')
cv2.createTrackbar('lh','image',0,180,nothing)
cv2.createTrackbar('ls','image',0,255,nothing)
cv2.createTrackbar('lv','image',0,255,nothing)
cv2.createTrackbar('hh','image',0,180,nothing)
cv2.createTrackbar('hs','image',0,255,nothing)
cv2.createTrackbar('hv','image',0,255,nothing)
while(True):
ret, img = cap.read()
hsv = cv2.cvtColor(img,cv2.COLOR_BGR2HSV)
lh = cv2.getTrackbarPos('lh','image')
ls = cv2.getTrackbarPos('ls','image')
lv = cv2.getTrackbarPos('lv','image')
hh = cv2.getTrackbarPos('hh','image')
hs = cv2.getTrackbarPos('hs','image')
hv = cv2.getTrackbarPos('lv','image')
lr = np.array([lh,ls,lv])
hr = np.array([hh,hs,hv])
mask = cv2.inRange(hsv,lr,hr)
res = cv2.bitwise_and(img,img,mask = mask)
cv2.imshow('image',res)
if(cv2.waitKey(1)&0xFF==ord(' ')):
break
cap.release()
cv2.destroyAllWindows()
The problem I encountered here is that when I run this program all I can see is a black screen. Even though I moved the trackbars, it was still showing black screen. But when I tried running this program removing the trackbars for higher range, by specifying the range in the code itself for a particular color it was working.
For example I changed the code :
hr = np.array([120,255,255])
mask = cv2.inRange(hsv,lr,hr)
How can I make this code work for all colors?
I have many skeletonized images like this:
How can i detect a cycle, a loop in the skeleton?
Are there "special" functions that do this or should I implement it as a graph?
In case there is only the graph option, can the python graph library NetworkX can help me?
You can exploit the topology of the skeleton. A cycle will have no holes, so we can use scipy.ndimage to find any holes and compare. This isn't the fastest method, but it's extremely easy to code.
import scipy.misc, scipy.ndimage
# Read the image
img = scipy.misc.imread("Skel.png")
# Retain only the skeleton
img[img!=255] = 0
img = img.astype(bool)
# Fill the holes
img2 = scipy.ndimage.binary_fill_holes(img)
# Compare the two, an image without cycles will have no holes
print "Cycles in image: ", ~(img == img2).all()
# As a test break the cycles
img3 = img.copy()
img3[0:200, 0:200] = 0
img4 = scipy.ndimage.binary_fill_holes(img3)
# Compare the two, an image without cycles will have no holes
print "Cycles in image: ", ~(img3 == img4).all()
I've used your "B" picture as an example. The first two images are the original and the filled version which detects a cycle. In the second version, I've broken the cycle and nothing gets filled, thus the two images are the same.
First, let's build an image of the letter B with PIL:
import Image, ImageDraw, ImageFont
image = Image.new("RGBA", (600,150), (255,255,255))
draw = ImageDraw.Draw(image)
fontsize = 150
font = ImageFont.truetype("/usr/share/fonts/truetype/liberation/LiberationMono-Regular.ttf", fontsize)
txt = 'B'
draw.text((30, 5), txt, (0,0,0), font=font)
img = image.resize((188,45), Image.ANTIALIAS)
print type(img)
plt.imshow(img)
you may find a better way to do that, particularly with path to the fonts. Ii would be better to load an image instead of generating it. Anyway, we have now something to work on:
Now, the real part:
import mahotas as mh
img = np.array(img)
im = img[:,0:50,0]
im = im < 128
skel = mh.thin(im)
noholes = mh.morph.close_holes(skel)
plt.subplot(311)
plt.imshow(im)
plt.subplot(312)
plt.imshow(skel)
plt.subplot(313)
cskel = np.logical_not(skel)
choles = np.logical_not(noholes)
holes = np.logical_and(cskel,noholes)
lab, n = mh.label(holes)
print 'B has %s holes'% str(n)
plt.imshow(lab)
And we have in the console (ipython):
B has 2 holes
Converting your skeleton image to a graph representation is not trivial, and I don't know of any tools to do that for you.
One way to do it in the bitmap would be to use a flood fill, like the paint bucket in photoshop. If you start a flood fill of the image, the entire background will get filled if there are no cycles. If the fill doesn't get the entire image then you've found a cycle. Robustly finding all the cycles could require filling multiple times.
This is likely to be very slow to execute, but probably much faster to code than a technique where you trace the skeleton into graph data structure.