Merge green screen image with a background image using Python - python

Hey guys I've been trying to merge 2 images(one is a green screen image with a object in the middle of the photo, the second one is a of a forest) using PIL. Both images are the same size(800, 600) I've tried to merge them into a new image but its seeming impossible at my end.
Any code solution?

I would break the problem down into pieces: first you're going to want to remove the green screen background from your first image. You can do this with PIL by iterating through every frame of the video and removing the green color. There are great guides to do so online.
Next, you're going to want to put the first image on top of the second. You can achieve that using the paste() method, also detailed in online guides.

I am also new in Python, but our instructor gave another way to get this done by using if conditions and nested for loops
*from PIL import Image
penguin_image = Image.open ("penguin.jpg")
beach_image = Image.open("beach.jpg")
penguin_width, penguin_height = penguin_image.size
#Accessing the pixels of the images
penguin_pixel = penguin_image.load()
beach_pixel = beach_image.load()
#Image inspection
(r0,g0,b0) = penguin_pixel[0,0]
(r1,g1,b1) = penguin_pixel[1,0]
(r2,g2,b2) = penguin_pixel[2,0]
(r3,g3,b3) = penguin_pixel[3,0]
(r4,g4,b4) = penguin_pixel[4,0]
(r5,g5,b5) = penguin_pixel[5,0]
(br,bg,bb) = beach_pixel[0,0]
print(penguin_width, penguin_height)
print(br, bg, bb)
#Creation of new image to hold combined pixels
combined_image = Image.new("RGB", penguin_image.size)
combined_pixel = combined_image.load()
#Checking for green
for ver in range (0, penguin_height):
for hor in range(0, penguin_width):
(r,g,b) = penguin_pixel[hor, ver]
(br,bg,bb) = beach_pixel[hor, ver]
if (r < 100 and g == 207 and b < 100):
combined_pixel[hor,ver] = (br, bg, bb)
else:
combined_pixel[hor,ver] = (r, g, b)*
combined_image.show()

Related

Why does this red color fill only one frame PIL?

Alright so I have some code that takes user input, fetches each letter's corresponding gif (a dancing letter) and stitches them together to form a word. Kind of like an animated font, basically.
My problem is that with PIL, it occasionally fills the background of one of the letters for around 1 frame with a red color, as demonstrated here:
This is my code that generates this
import sys
from PIL import Image
from PIL import GifImagePlugin
from io import BytesIO
def text(str, procid):
t = []
for c in str:
t.append('letters/' + c + '.gif')
images = [Image.open(x) for x in t]
widths, heights = zip(*(i.size for i in images))
total_width = sum(widths)
max_height = max(heights)
x_offset = 0
imgs = []
for frame in range(0, images[0].n_frames):
new = Image.new('RGBA', (total_width, max_height))
x_offset = 0
for im in images:
im.seek(frame)
new.paste(im, (x_offset,0))
x_offset += im.size[0]
imgs.append(new)
img_io = BytesIO()
new.save(img_io, 'GIF', transparency=0, save_all=True, append_images=imgs, loop=0, disposal=2,duration=1,optimize=True)
img_io.seek(0)
return img_io
Any ideas as to why it does this, and is it possible to fix this?
Edit: I've attached the input GIFS used to create the animated "TEST"
e.gif https://i.stack.imgur.com/PQfMK.gif
s.gif https://i.stack.imgur.com/7PQp5.gif
t.gif https://i.stack.imgur.com/DMLrg.gif
Edit 2: All the letters have the same amount of frames, and the red background is not present anywhere in S.gif meaning it has to be a PIL problem or a problem with my code.
I was able to fix it by downloading a different set of the same dancing letters from a somewhat more reputable source. (The previous ones were uploaded to tenor so it could've been modified in some ways)
to me there is more going on,
check one of the s.gif frames :
and nevertheless my output gif using your code looks like:

How to change color of an overlay image?

I'm trying to set the cactus image over the desert image. And have the green background of the cactus removed.
Here is my code:
from PIL import Image
image_desert = Image.open("desert.jpg")
image_cactus = Image.open("cactus.jpg")
image_cactus = image_desert.load()
for y in range (200, 300):
for x in range (100, 200):
(r, g, b) = image_cactus[x, y]
newgreen = g + 30
#choosing rgb colors
image_cactus[x, y] = (r, newgreen, b)
image_desert.show()
Any help would be appreciated it.
I thought this was interesting. I've never worked with picture manipulation before, so I wanted to give it a shot and see if I could figure it out. Let me know if this works for your purposes, or if it needs to be improved.
from PIL import Image
image_desert = Image.open("desert.jpg")
image_cactus = Image.open("cactus.jpg")
image_temp = image_desert.load()
image_cactus = image_cactus.load()
(rCactus, gCactus, bCactus) = image_cactus[0, 0]
#This gets the rgb value of the green background
for y in range (0, 600):
for x in range (0, 600):
(rResult, gResult, bResult) = image_cactus[x, y]
if(gCactus <= gResult + 5 and gCactus >= gResult - 5):
#this is a comparison of the cactus picture with the background pixel value.
# There is a range of plus or minus 5 since the pixels change ever so slightly
# near the edge of the cactus, so the range makes it look a little better.
(rResult, gResult, bResult) = image_temp[x, y]
#if the pixel in the cactus photo is within a range of 10 pixels of the
#known green background pixel value, it will be replaced with the pixel
#from the desert photo.
image_temp[x, y] = (rResult, gResult, bResult)
image_desert.show()
It's definitely not a perfect picture, photoshop would do a much better job. Getting rid of all of the green around the cactus is where I'm running into the biggest issue. The wider you make the range of pixels, the more your cactus may start disappearing and getting spotty.

How to remove unwanted parts in an image and then create a new small image using Python?

I want to remove the dark(black strips) and also the white curves in the image, and then align the remained parts connected in a new small-sized image, making the colored parts looks continuously. I hope can get some suggestions for solutions.
I have tried to use PIL to read the image.
I don't know how to set the right threshold and resize the image
I'm not an expert at all in image processing, but let me know if this is enough for you.
Looking at the brightness (sum of the RGB values) distribution, maybe one option is to just filter pixel based on their value:
It looks like the dark parts have a brightness below 100 (or something like that). I filtered it this way:
from PIL import Image
import numpy as np
def filter_image(img,threshold=100):
data = np.array(img.convert('RGB'))
brightness = np.sum(data,axis=2)
filtered_img = data.copy()*0
for i in range(data.shape[0]):
k = 0 # k index the columns that are bright enough
for j in range(data.shape[1]):
if brightness[i,j] > threshold:
filtered_img[i,k,:] = data[i,j,:]
k += 1 # we increment only if it's bright enough
# End of column iterator. The right side of the image is black
return Image.fromarray(filtered_img)
img = Image.open("test.png")
filtered = filter_image(img)
filtered.show()
I get the following result. I'm sure experts can do much better, but it's a start:
The following is only looking for black pixels, as can be seen by the first image, many of the pixels you want out are not black. You will need to find a way to scale up what you will take out.
Also, research will need to be done on collapsing an image, as can be seen by my collapse. Although, this image collapse may work if you are able to get rid of everything but the reddish colors. Or you can reduce by width, which is what the third picture shows.
from PIL import Image, ImageDraw
def main():
picture = Image.open('/Volumes/Flashdrive/Stack_OverFlow/imageprocessing.png', 'r')
# pix_val = list(im.getdata())
# print(pix_val)
# https://code-maven.com/create-images-with-python-pil-pillowimg = Image.new('RGB', (100, 30), color = (73, 109, 137))
blackcount = 0
pix = picture.convert('RGB') # https://stackoverflow.com/questions/11064786/get-pixels-rgb-using-pil
width, height = picture.size
img = Image.new('RGB', (width, height), color=(73, 109, 137))
newpic = []
for i in range(width):
newpictemp = []
for j in range(height):
# https://stackoverflow.com/questions/13167269/changing-pixel-color-python
r, g, b = pix.getpixel((i, j))
if r == 0 and g == 0 and b == 0:
blackcount += 1
else:
img.putpixel((i, j), (r, g, b))
newpictemp.append((r, g, b))
newpic.append(newpictemp)
img.save('pil_text.png')
newheight = int(((width * height) - blackcount) / width)
print(newheight)
img2 = Image.new('RGB', (width, newheight), color=(73, 109, 137))
for i in range(width):
for j in range(newheight):
try:
z = newpic[i][j]
img2.putpixel((i, j), newpic[i][j])
except:
continue
img2.save('pil_text2.png')
if __name__ == "__main__":
main()
No black pixels on left, removed black pixels on right, remove and resize by width (height resize shown in code)

Python Opencv2: Background appearing on a no background image after adding it on webcam frame

I am writing a Python script using opencv2 that takes my webcam frame (img) and adds glasses on my face, but the glasses image is somehow changed and i can't see why.
Here is the code snippet that adds the two images:
# Load two images
ret, img = cap.read()
img2 = cv2.imread('glasses_2.png', -1)
img2 = img2[:,:,0:3]
img2 = cv2.resize(img2, (glasses_width, glasses_height))
rows,cols,channels = img2.shape
for i in range(0, rows):
for j in range(0, cols):
if img2[i, j][2] != 0:
img[round(glasses_center_y - glasses_height/2) + i, round(glasses_center_x - glasses_width/2) + j] = img2[i, j]
This is the glass that i want to display
And this is the actual result
Can anybody see where is the problem? A searched a lot and didn't find anything helpful. Is it related to color channels?
Thanks
Try changing your if statement to equal to zero instead of not equal to.

How can i find cycles in a skeleton image with python libraries?

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.

Categories

Resources