I am trying to write a method that will fill in a given shape so that it becomes solid black.
Example:
This octagon which initially is only an outline, will turn into a solid black octagon, however this should work with any shape as long as all edges are closed.
Octagon
def img_filled(im_1, im_2):
img_fill_neg = ImageChops.subtract(im_1, im_2)
img_fill = ImageOps.invert(img_fill_neg)
img_fill.show()
I have read the docs 10x over and have found several other ways to manipulate the image, however I can not find an example to fill in a pre-existing shape within the image. I see that using floodfill() is an option, although I'm not sure how to grab the shape I want to fill.
Note: I do not have access to any other image processing libraries for this task.
There are several ways of doing this. You could do as I do here, and fill all the areas outside the outline with magenta, then make everything that is not magenta into black, and then revert all artificially magenta-coloured pixels to white.
I have interspersed intermediate images in the code, but you can just grab all the bits of code and collect them together in order to have a working lump of code.
#!/usr/bin/env python3
from PIL import Image, ImageDraw
import numpy as np
# Open the image
im = Image.open('octagon.png').convert('RGB')
# Make all background (exterior to octagon) pixels magenta (255,0,255)
ImageDraw.floodfill(im,xy=(0,0),value=(255,0,255),thresh=200)
# DEBUG
im.save('intermediate.png')
# Make everything not magenta black
n = np.array(im)
n[(n[:, :, 0:3] != [255,0,255]).any(2)] = [0,0,0]
# Revert all artifically filled magenta pixels to white
n[(n[:, :, 0:3] == [255,0,255]).all(2)] = [255,255,255]
Image.fromarray(n).save('result.png')
Or, you could fill all the background with magenta, then locate a white pixel and flood-fill with black using that white pixel as a seed. The method you choose depends on the expected colours of your images, and the degree to which you wish to preserve anti-aliasing and so on.
Related
I'm trying to write a program on linux that does something if the pixels in an area aren't all the same color, for example:
if color not "255, 255, 255":
#do something
this is what i have for one pixel:
import time, pyautogui
time.clock()
image = pyautogui.screenshot()
color = image.getpixel((1006, 553))
print(time.clock())
print(color)
I know how to get the color of a pixel using .getpixel() but that only gets one pixel
Basically, how do i get the color of an area of pixels when i know all the pixels in that area are the same color.
Also, as quick as possible, like 0.5s or under.
I keep recommending it, but the scikit-image library is pretty great, and they have some really solid documentation and examples. I would recommend a combo of that and using numpy arrays directly. It is just a lot faster when working directly with pixels.
You will have to convert the PIL image to a numpy array...but this should work with that:
import pyautogui
import numpy as np
image = pyautogui.screenshot()
np_image = np.array(image)
You can slice the image:
red_slice = np_image[0:50, 0:50,0]
red_mask = red_slice == 200
This would give you the values for red in the upper right 50x50 pixel area. red_mask is an array of True/False values whether each red value in that area is equal to 200. This can be repeated for the other channels as you see fit.
I'm trying to remove the background from product images, save them as transparent png's and got to a point where I can't figure out how and why I get the white line around the products like a fuzziness(see second image) don't know the real word for the effect. Also I'm losing the Nike swoosh which is white too :(
from PIL import Image
img = Image.open('test.jpg')
img = img.convert("RGBA")
datas = img.getdata()
newData = []
for item in datas:
if item[0] > 247 and item[1] > 247 and item[2] > 247:
newData.append((255, 255, 255, 0))
else:
newData.append(item)
img.putdata(newData)
img.save("test.png", "PNG")
Any ideas how I can fix this so I get clean selections, edges ?
Take a copy of your image and use PIL/Pillow's ImageDraw.floodfill() to flood fill from the top-left corner using a reasonable tolerance - that way you will only fill to the edges of the shirt and avoid the Nike logo.
Then take the background outline and make it white and everything else black and try applying some morphology (from scikit-image maybe) to dilate the white a little larger to hide the jaggies.
Finally, put the resulting new layer into the image with putalpha().
I am really pushed for time, but here are the bones of it. Just missing the copy of the original image at the start and the putalpha() of the new alpha layer back at the end...
from PIL import Image, ImageDraw
import numpy as np
import skimage.morphology
# Open the shirt
im = Image.open('shirt.jpg')
# Make all background pixels (not including Nike logo) into magenta (255,0,255)
ImageDraw.floodfill(im,xy=(0,0),value=(255,0,255),thresh=10)
# DEBUG
im.show()
Experiment with the threshold (thresh) here. If you make it 50, it works much more cleanly and may be good enough to stop.
# Make into Numpy array
n = np.array(im)
# Mask of magenta background pixels
bgMask =(n[:, :, 0:3] == [255,0,255]).all(2)
# DEBUG
Image.fromarray((bgMask*255).astype(np.uint8)).show()
# Make a disk-shaped structuring element
strel = skimage.morphology.disk(13)
# Perform a morphological closing with structuring element
closed = skimage.morphology.binary_closing(bgMask,selem=strel)
# DEBUG
Image.fromarray((closed*255).astype(np.uint8)).show()
If you are unfamiliar with morphology, Anthony Thyssen has some excellent noes worth reading here.
By the way, you could also use potrace to smooth the outline somewhat.
I had a bit more time today so here is a more complete version. You can experiment with the morphology disk sizes and floodfill thresholds according to your images till you find something tailored for your needs:
#!/bin/env python3
from PIL import Image, ImageDraw
import numpy as np
import skimage.morphology
# Open the shirt and make a clean copy before we dink with it too much
im = Image.open('shirt.jpg')
orig = im.copy()
# Make all background pixels (not including Nike logo) into magenta (255,0,255)
ImageDraw.floodfill(im,xy=(0,0),value=(255,0,255),thresh=50)
# DEBUG
im.show()
# Make into Numpy array
n = np.array(im)
# Mask of magenta background pixels
bgMask =(n[:, :, 0:3] == [255,0,255]).all(2)
# DEBUG
Image.fromarray((bgMask*255).astype(np.uint8)).show()
# Make a disk-shaped structuring element
strel = skimage.morphology.disk(13)
# Perform a morphological closing with structuring element to remove blobs
newalpha = skimage.morphology.binary_closing(bgMask,selem=strel)
# Perform a morphological dilation to expand mask right to edges of shirt
newalpha = skimage.morphology.binary_dilation(newalpha, selem=strel)
# Make a PIL representation of newalpha, converting from True/False to 0/255
newalphaPIL = (newalpha*255).astype(np.uint8)
newalphaPIL = Image.fromarray(255-newalphaPIL, mode='L')
# DEBUG
newalphaPIL.show()
# Put new, cleaned up image into alpha layer of original image
orig.putalpha(newalphaPIL)
orig.save('result.png')
As regards using potrace to smooth the outline, you would save new alphaPIL as a PGM format image because that is what potrace likes as input. So that would be:
newalphaPIL.save('newalpha.pgm')
Now you can play around, oops I meant "experiment carefully" with potrace to smooth the alpha outline. The basic command is:
potrace -b pgm newalpha.pgm -o smoothalpha.pgm
You can then re-load the image smoothalpha.pgm back into your Python and use it on the last line in the putalpha() call. Here is an animation of the difference between the original unsmoothed alpha and the smoothed one:
Look carefully at the edges to see the difference. You may want to experiment with resizing the alpha either to twice the size or half the size before smoothing to see what effect that has.
I have two images, one overlay and one background.
I want to create a new image, by editing overlay image and manipulating it to show only the pixels which have blue colour in the background image.
I dont want to add the background, it is only for selecting the pixels.
Rest part should be transparent.
Any hints or ideas please? PS: I edited result image with paint so its not perfect.
Image 1 is background image.
Image 2 is overlay image.
Image 3 is the check I want to perform. (to find out which pixels have blue in background and making remaining pixels transparent)
Image 4 is the result image after editing.
I renamed your images according to my way of thinking, so I took this as image.png:
and this as mask.png:
Then I did what I think you want as follows. I wrote it quite verbosely so you can see all the steps along the way:
#!/usr/local/bin/python3
from PIL import Image
import numpy as np
# Open input images
image = Image.open("image.png")
mask = Image.open("mask.png")
# Get dimensions
h,w=image.size
# Resize mask to match image, taking care not to introduce new colours (Image.NEAREST)
mask = mask.resize((h,w), Image.NEAREST)
mask.save('mask_resized.png')
# Convert both images to numpy equivalents
npimage = np.array(image)
npmask = np.array(mask)
# Make image transparent where mask is not blue
# Blue pixels in mask seem to show up as RGB(163 204 255)
npimage[:,:,3] = np.where((npmask[:,:,0]<170) & (npmask[:,:,1]<210) & (npmask[:,:,2]>250),255,0).astype(np.uint8)
# Identify grey pixels in image, i.e. R=G=B, and make transparent also
RequalsG=np.where(npimage[:,:,0]==npimage[:,:,1],1,0)
RequalsB=np.where(npimage[:,:,0]==npimage[:,:,2],1,0)
grey=(RequalsG*RequalsB).astype(np.uint8)
npimage[:,:,3] *= 1-grey
# Convert numpy image to PIL image and save
PILrgba=Image.fromarray(npimage)
PILrgba.save("result.png")
And this is the result:
Notes:
a) Your image already has an (unused) alpha channel present.
b) Any lines starting:
npimage[:,:,3] = ...
are just modifying the 4th channel, i.e. the alpha/transparency channel of the image
I can successfully convert a rectangular image into a png with transparent rounded corners like this:
However, when I take this transparent cornered image and I want to use it in another image generated with Pillow, I end up with this:
The transparent corners become black. I've been playing around with this for a while but I can't find any way in which the transparent parts of an image don't turn black once I place them on another image with Pillow.
Here is the code I use:
mask = Image.open('Test mask.png').convert('L')
im = Image.open('boat.jpg')
im.resize(mask.size)
output = ImageOps.fit(im, mask.size, centering=(0.5, 0.5))
output.putalpha(mask)
output.save('output.png')
im = Image.open('output.png')
image_bg = Image.new('RGBA', (1292,440), (255,255,255,100))
image_fg = im.resize((710, 400), Image.ANTIALIAS)
image_bg.paste(image_fg, (20, 20))
image_bg.save('output2.jpg')
Is there a solution for this? Thanks.
Per some suggestions I exported the 2nd image as a PNG, but then I ended up with an image with holes in it:
Obviously I want the second image to have a consistent white background without holes.
Here is what I actually want to end up with. The orange is only placed there to highlight the image itself. It's a rectangular image with white background, with a picture placed into it with rounded corners.
If you paste an image with transparent pixels onto another image, the transparent pixels are just copied as well. It looks like you only want to paste the non-transparent pixels. In that case, you need a mask for the paste function.
image_bg.paste(image_fg, (20, 20), mask=image_fg)
Note the third argument here. From the documentation:
If a mask is given, this method updates only the regions indicated by
the mask. You can use either "1", "L" or "RGBA" images (in the latter
case, the alpha band is used as mask). Where the mask is 255, the
given image is copied as is. Where the mask is 0, the current value
is preserved. Intermediate values will mix the two images together,
including their alpha channels if they have them.
What we did here is provide an RGBA image as mask, and use the alpha channel as mask.
I'd like to create a game, similar to that of Geometry Dash. I have all the images for the cubes, but they are all grey and white - this is to allow the user to select the colours.
I have two variables, colour_1 and colour_2. colour_1 should be in the grey, and colour_2 should be in the white. If I say what the variables are, how would I modify the image to have the right colours?
The colours on the images are not all the same, the edges blend, so that the image is smoother. This may cause complications.
I found this on the website Fishstick proposed
Here's a working code based on it
img_surface = pygame.image.load("image.gif") # Load image on a surface
img_array = pygame.surfarray.array3d(img_surface) # Convert it into an 3D array
colored_img = numpy.array(img_array) # Array thing
colored_img[:, :, 0] = 255 # <-- Red
colored_img[:, :, 1] = 128 # <-- Green
colored_img[:, :, 2] = 0 # <-- Blue
img_surface = pygame.surfarray.make_surface(colored_img) # Convert it back to a surface
screen.blit(img_surface, (0, 0)) # Draw it on the screen
This change the color value of each pixel. If you set red to 255, it will add 255 to red for all pixels. But if you set 255 to all colors the image will be white.
Note that you need to install NumPy to use this and you can do so by doing this:
pip install numpy
Also you could try replacing the last two lines for
pygame.surfarray.blit_array(screen, colored_img)
Which should be faster but it didn't work for me so I converted the array into a surface then blitted it on the screen.
If that doesn't answer your question maybe these will:
Pygame - Recolor pixes of a certain color to another using SurfArray (Array slicing issue)
https://gamedev.stackexchange.com/questions/26550/how-can-a-pygame-image-be-colored
Pygame Surfarray Module Documentation
http://www.pygame.org/docs/ref/surfarray.html#pygame.surfarray.blit_array
Write classes and instantiate the objects with color code variables.
You can write a method that will draw the shape/image in combination with state-specific data.