Copy black pixels from image and paste in same image with offset - python

I've been trying to get some masking to work in my pictures, but I guess there has to be an easier way to do this:
a) I have an BW picture (photo) showing numbers from a display, "test.png" ( 1000x300 px)
b) I want to copy (only) the black pixels and paste them in the same image
c) When pasting, I want the paste to be offset by its "original" place by 20px (both x/y)
I try running the code below but get an error:
import cv2
test = Image.open('test.png')
np = Image.new('1', (1000, 300), 255)
mask = np.bitwise_and(test, np.roll(test, 20, (0,1)))
mask.save('mask.png')
I get AttributeError: 'Image' object has no attribute 'bitwise_and'

If image is logical (0/1) then:
res=np.bitwise_and(image, np.roll(image, 20, (0,1)))

You can do it like this:
from PIL import Image
import numpy as np
# Load image and make Numpy array
im = Image.open('image.png').convert('L')
na = np.array(im)
# Get x,y coordinates of black pixels
Y, X = np.where(na==0)
# Make pixels 30 across and 30 down from them black
na[Y+30, X+30] = 0
# Convert back to PIL Image and save
Image.fromarray(na).save('result.png')

Related

How to check whether a jpeg image is color or grayscale using only Python PIL? (not using OpenCV)

I found this method really helpful and it's actually working quite accurately. BUT this uses OpenCV.. and I want to use the same method using PIL.
code using PIL instead of OpenCV:
from PIL import Image
import numpy as np
###test image
img=Image.open('')
img=img.load()
### splitting b,g,r channels
r,g,b=img.split()
### getting differences between (b,g), (r,g), (b,r) channel pixels
r_g=np.count_nonzero(abs(r-g))
r_b=np.count_nonzero(abs(r-b))
g_b=np.count_nonzero(abs(g-b))
### sum of differences
diff_sum=float(r_g+r_b+g_b)
### finding ratio of diff_sum with respect to size of image
ratio=diff_sum/img.size
if ratio>0.005:
print("image is color")
else:
print("image is greyscale")
I changed cv2.imread('') to Image.open('') and added img=img.load().
and I changed b,g,r=cv2.split(img) to r,g,b=img.split()
I know that split() method exists in PIL. but I'm having this error.
AttributeError: 'PixelAccess' object has no attribute 'split'
How can I solve this?
Thank you in advance!!
You are mixing data types like you are mixing Red Bull and Vodka.
The load method is producing the error because it converts the PIL image into a PixelAccess object, an you need a PIL image for split(). Also, count_nonzero() does not work because it operates on NumPy arrays, and you are attempting to call that method on a PIL image. Lastly, size returns a tuple (width and height) of the image, so you need to modify your code accordingly:
from PIL import Image
import numpy as np
###test image
img=Image.open("D://opencvImages//lena512.png")
### splitting b,g,r channels
r,g,b=img.split()
### PIL to numpy conversion:
r = np.array(r)
g = np.array(g)
b = np.array(b)
### getting differences between (b,g), (r,g), (b,r) channel pixels
r_g=np.count_nonzero(abs(r-g))
r_b=np.count_nonzero(abs(r-b))
g_b=np.count_nonzero(abs(g-b))
### sum of differences
diff_sum=float(r_g+r_b+g_b)
### get image size:
width, height = img.size
### get total pixels on image:
totalPixels = width * height
### finding ratio of diff_sum with respect to size of image
ratio = diff_sum/totalPixels
print("Ratio is: "+ratio)
if ratio>0.005:
print("image is color")
else:
print("image is greyscale")
Let's check out the Lena image in color and grayscale:
Color Lena returns this:
Ratio is: 2.981109619140625
image is color
And Grayscale Lena returns this:
Ratio is: 0.0
image is greyscale

Convert a large grayscale PIL image to black and transparent

I am trying to use a large 2d array to create an image mask with black and transparent parts. Originally, the input 2d array was a PIL.Image that was loaded in grayscale ('L') mode. So it contains values between 0 and 255. And now I want to replace all the 0s with [0,0,0,255] (black stays black) and all values >0 should be [0,0,0,0] (transparent). I can do this simply like this:
import numpy as np
# generate some random test data - normally I just read the input image, which is fast
input_data = np.array([np.array([random.choice([0,10]) for x in range(22000)]) for y in range(9000)])
# create a new img containing black and transparent pixels (r,g,b,alpha) and this takes ages
overlay_img = [[[0, 0, 0, 255] if input_data[y][x] == 0 else [0, 0, 0, 0] for x in range(len(input_data[0]))] for y in range(len(input_data))]
overlay_img = np.array(overlay_img)
This takes quite some time because the input data is so large (~22000x9000). I am curious if it is somehow possible to do this faster. I also tried np.where, but I could not get it to work. Maybe there is even a way to directly change the PIL image?
fyi: In the end, I just want to plot this image on top of my matplotlib plot with imshow, so that only the relevant regions are visible (where the image is transparent) and the rest is hidden/black.
Here just a very quick and small example of what I want to do:
I think you want this, but you haven't shown your code for imshow():
#!/usr/bin/env python3
import random
import numpy as np
# Set up dimensions and random input image
h, w = 9000, 22000
im = np.random.randint(0, 11, (h,w), dtype=np.uint8)
# Create 4-channel mask image
mask = np.zeros((h,w,4), dtype=np.uint8)
mask[...,3] = (im==0) * 255
The last line takes 800ms on my MacBook Pro.
If you need a bit more performance, you can use numexpr as follows and the time required is 300ms instead of 800ms:
import random
import numexpr as ne
import numpy as np
# Set up dimensions and random input image
h, w = 9000, 22000
im = np.random.randint(0, 11, (h,w), dtype=np.uint8)
# Create 4-channel mask image
mask = np.zeros((h,w,4), dtype=np.uint8)
# Same but with "numexpr"
mask[...,3] = ne.evaluate("(im==0)*255")

How do I resize image but maintain features in the image? Python

I have an image that looks like this:
array.resize(20,20,3)
img = Image.fromarray(array, 'RGB')
img.save('my.png',quality=90)
img.show()
It is currently a 500x500x3 NumPy array. The underlying space is a 20x20 grid of cells and I want to resize the image so that each grid cell has entries in a 20x20x3 NumPy Array corresponding to it's RGB values instead of (500/20)*(500/20)*3 entries per cell.
The code above does not seem to work unfortunately as it seems to be giving more entries per cell than I expected although I am not 100% sure.
To resize image with pillow you can use Image.resize()
from PIL import Image
import urllib.request
import numpy as np
data = urllib.request.urlopen('https://i.stack.imgur.com/7bPlZ.png')
old_img = Image.open(data)
new_img = old_img.resize((20, 20))
new_img.save('my.png',quality=90)
new_img.show()
array = np.array(new_img)
print(array)
But resizing image you can create pixels with half-tones.
Maybe you should get values directly from numpy.array. You have solid colors so you could get single pixel from every cell - because every cell has size 25x25 so it could be:
new_array = old_array[::25,::25,:]
and then you don't have to convert to image.
And if you convert this array to image then it should be sharper than create with Image.resize.
from PIL import Image
import urllib.request
import numpy as np
data = urllib.request.urlopen('https://i.stack.imgur.com/7bPlZ.png')
old_img = Image.open(data)
old_array = np.array(old_img)
new_array = array[::25,::25,:]
print(new_array)
new_img = Image.fromarray(new_array)
new_img.save('my.png',quality=90)
new_img.show()
Try this
size = 20, 20
img = Image.fromarray(array, 'RGB')
img.thumbnail(size, Image.ANTIALIAS)
img.save('my.png',quality=90)
img.show()

Resizing JPG using PIL.resize gives a completely black image

I'm using PIL to resize a JPG. I'm expecting the same image, resized as output, but instead I get a correctly sized black box. The new image file is completely devoid of any information, just an empty file. Here is an excerpt for my script:
basewidth = 300
img = Image.open(path_to_image)
wpercent = (basewidth/float(img.size[0]))
hsize = int((float(img.size[1])*float(wpercent)))
img = img.resize((basewidth,hsize))
img.save(dir + "/the_image.jpg")
I've tried resizing with Image.LANCZOS as the second argument, (defaults to Image.NEAREST with 1 argument), but it didn't make a difference. I'm running Python3 on Ubunutu 16.04. Any ideas on why the image file is empty?
I also encountered the same issue when trying to resize an image with transparent background. The "resize" works after I add a white background to the image.
Code to add a white background then resize the image:
from PIL import Image
im = Image.open("path/to/img")
if im.mode == 'RGBA':
alpha = im.split()[3]
bgmask = alpha.point(lambda x: 255-x)
im = im.convert('RGB')
im.paste((255,255,255), None, bgmask)
im = im.resize((new_width, new_height), Image.ANTIALIAS)
ref:
Other's code for making thumbnail
Python: Image resizing: keep proportion - add white background
The simplest way to get to the bottom of this is to post your image! Failing that, we can check the various aspects of your image.
So, import Numpy and PIL, open your image and convert it to a Numpy ndarray, you can then inspect its characteristics:
import numpy as np
from PIL import Image
# Open image
img = Image.open('unhappy.jpg')
# Convert to Numpy Array
n = np.array(img)
Now you can print and inspect the following things:
n.shape # we are expecting something like (1580, 1725, 3)
n.dtype # we expect dtype('uint8')
n.max() # if there's white in the image, we expect 255
n.min() # if there's black in the image, we expect 0
n.mean() # we expect some value between 50-200 for most images

How to create a white image in Python?

Upon doing my homework, I stumbled across a problem concerning Python and image manipulation. I must say, using the Image lib is not an option. So here it is
from scipy.misc import imread,imsave
from numpy import zeros
imga = zeros([100,100,3])
h = len(imga)
w = len(imga[0])
for y in range(h):
for x in range(w):
imga[y,x] = [255,255,255]
imsave("Result.jpg",imga)
I would assume it makes my picture white, but it turns it black, and I have no idea why
It's not about the code (and I know it looks very ugly). Its just about the fact, that it is a black image.
Every color in an image is represented by one byte. So to create an image array, you should set it's dtype to uint8.
And, you don't need for-loop to set every elements to 255, you can use fill() method or slice index:
import numpy as np
img = np.zeros([100,100,3],dtype=np.uint8)
img.fill(255) # or img[:] = 255
Easy!
Check the below Code:
whiteFrame = 255 * np.ones((1000,1000,3), np.uint8)
255 is the color for filling the bytes.
1000, 1000 is the size of the image.
3 is the color channel for the image.
And unit8 is the type
Goodluck
Here's a simple way to create a white image with a python one liner.
$ python3 -c "from PIL import Image;Image.new('RGB', (1900, 1080), color = (255,255,255)).save('Img.jpg')"
This will create a white image with a width of 1900 and hight of 1080.
When creating imga, you need to set the unit type. Specifically, change the following line of code:
imga = zeros([100,100,3], dtype=np.uint8)
And, add the following to your imports:
import numpy as np
That gives a white image on my machine.
The headline is too broad and shows up at Google first. I needed a white image and used PIL and numpy. PILlow actually works well with numpy
import numpy as np
from PIL import Image
img = np.zeros([100,100,3],dtype=np.uint8)
img.fill(255) # numpy array!
im = Image.fromarray(img) #convert numpy array to image
im.save('whh.jpg')
Just regarding the headline of this question, I did need a white image as well as a pillow input. And the solutions presented here did not work for me.
Therefore here a different way to generate white images for other purposes:
from PIL import Image
img = Image.new('RGB', (200, 50), color = (255,255,255))
Size and color may be changed in the 2nd and 3rd parameter of the Image.new()-function.
And if you want to write something on this image or save it, this would be example code for this.
from PIL import ImageFont, ImageDraw
fnt = ImageFont.truetype("Pillow/Tests/fonts/FreeMono.ttf", 30)
ImageDraw.Draw(img).text((0,0), "hello world", font=fnt, fill=(0,0,0))
img.save('test.jpg')
# Create an array with a required colours
# The colours are given in BGR [B, G, R]
# The array is created with values of ones, the size is (H, W, Channels)
# The format of the array is uint8
# This array needs to be converted to an image of type uint8
selectedColor = [75, 19, 77] * np.ones((640, 480, 3), np.uint8)
imgSelectedColor = np.uint8(np.absolute(selectedColor))

Categories