Conditionnally assign pixel values of one image to another - python

I have two images ( of the same size): A and B
A is the mask, it contains regions that have zero value and others that have RGB values.
B is the RGB image that i want to change the values of some of its pixels to their correspondent A's pixels (pixels that have the same position and that are different from zero).
I think it would be something like this:
if A(i,j) <>0 then B(i,j)=A(i,j)
except that i don't know how to write it in python...
can anyone help?

If you read the images with opencv:
h = b.shape[0]
w = b.shape[1]
for y in range(0, h):
for x in range(0, w):
if a[y,x] > 0:
b[y,x] = a[y,x]
Or better, as points #Dan MaĊĦek in the comment
import numpy as np
def apply_mask(img, mask):
img = np.where(mask > 0, mask, img)
return img
Notice that in numpy arrays, the height comes first in shape. Opencv loads the image into numpy arrays.

To apply the mask for src, you can use cv2.bitwise_and:
cv2.bitwise_and(src, src, mask=mask)

Related

How to generate a mask using Pillow's Image.load() function

I want to create a mask based on certain pixel values. For example: every pixel where B > 200
The Image.load() method seems to be exactly what I need for identifying the pixels with these values, but I can't seem to figure out how to take all these pixels and create a mask image out of them.
R, G, B = 0, 1, 2
pixels = self.input_image.get_value().load()
width, height = self.input_image.get_value().size
for y in range(0, height):
for x in range(0, width):
if pixels[x, y][B] > 200:
print("%s - %s's blue is more than 200" % (x, y))
``
I meant for you to avoid for loops and just use Numpy. So, starting with this image:
from PIL import Image
import numpy as np
# Open image
im = Image.open('colorwheel.png')
# Make Numpy array
ni = np.array(im)
# Mask pixels where Blue > 200
blues = ni[:,:,2]>200
# Save logical mask as PNG
Image.fromarray((blues*255).astype(np.uint8)).save('result.png')
If you want to make the masked pixels black, use:
ni[blues] = 0
Image.fromarray(ni).save('result.png')
You can make more complex, compound tests against ranges like this:
#!/usr/bin/env python3
from PIL import Image
import numpy as np
# Open image
im = Image.open('colorwheel.png')
# Make Numpy array
ni = np.array(im)
# Mask pixels where 100 < Blue < 200
blues = ( ni[:,:,2]>100 ) & (ni[:,:,2]<200)
# Save logical mask as PNG
Image.fromarray((blues*255).astype(np.uint8)).save('result.png')
You can also make a condition on Reds, Greens and Blues and then use Numpy's np.logical_and() and np.logical_or() to make compound conditions, e.g.:
bluesHi = ni[:,:,2] > 200
redsLo = ni[:,:,0] < 50
mask = np.logical_and(bluesHi,redsLo)
Thanks to the reply from Mark Setchell, I solved by making a numpy array the same size as my image filled with zeroes. Then for every pixel where B > 200, I set the corresponding value in the array to 255. Finally I converted the numpy array to a PIL image in the same mode as my input image was.
R, G, B = 0, 1, 2
pixels = self.input_image.get_value().load()
width, height = self.input_image.get_value().size
mode = self.input_image.get_value().mode
mask = np.zeros((height, width))
for y in range(0, height):
for x in range(0, width):
if pixels[x, y][2] > 200:
mask[y][x] = 255
mask_image = Image.fromarray(mask).convert(mode)

Iterate with for loop through binarized image possible?

this is my python code:
import cv2
img = cv2.imread("foo.jpg")
#here I can iterate trough each pixel since I have a 2D array
for x in range(img.shape[0]):
for y in range(img.shape[1]):
pass #maipulate each pixel
gary = cv2.cvtColor(img, COLOR_BGR2GRAY)
bin = cv2.threshold(gray,127,255,cv2.THRESH_BINARY)
#here I can not iterate trough each pixel since I have a 1D array
for x in range(img.shape[0]):
for y in range(img.shape[1]):
pass
My Question:
How can I iterate through each pixel of the binary image?
I want to use a sliding window search algorithm.
Your code doesn't work because threshold() returns a tuple of 2 values: the threshold value you set (127) and a binary image. If you seperate these you can use the same double loop to access each value / pixel.
I've modified your code, as there where also a couple of typo's.
import cv2
img = cv2.imread("foo.jpg")
#here I can iterate trough each pixel since I have a 2D array
for x in range(img.shape[0]):
for y in range(img.shape[1]):
pass #maipulate each pixel
gray= cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
thresh, bin_img = cv2.threshold(gray,127,255,cv2.THRESH_BINARY)
for x in range(bin_img.shape[0]):
for y in range(bin_img.shape[1]):
pass

Get all pixels different from a color and paint them in other image

I have image A with dimension (512, 512, 3).
I want to find all the pixels which != [255,255,255].
Given that pixels, I want to color these coordinates in another image B.
What am I doing wrong?
indices = np.where(imgA!= [255,255,255])
imgB[indices] = [0,0,0]
This template should get you on the right path:
from PIL import image
picture = Image.open(path_to_picture)
width, height = picture.size
for x in range(width):
for y in range(height):
current_color = picture.getpixel( (x,y) )
if current_color[0:3]!=(255,255,255):
picture.putpixel( (x,y), (***, ***,***) + (current_color[-1],))
picture.save(path_to_new_picture)
Note here that getpixel() will return a tuple that contains the RGBA values for the given pixel. In this example, I am assuming that you are retaining the alpha value and simply modifying the RGB values of the current pixel.
you need to loop over each pixel in the image.
... imgA!= [255,255,255] will always return true, because you are comparing a (512,512,3) nd.array to a (3,) nd.array
Even if your images are not built from numpy matricies, this point still applies. If you run into performance issues, use cython for faster for loops.

Python -- change the RGB values of the image and save as a image

I can read every pixel' RGB of the image already, but I don't know how to change the values of RGB to a half and save as a image.Thank you in advance.
from PIL import *
def half_pixel(jpg):
im=Image.open(jpg)
img=im.load()
print(im.size)
[xs,ys]=im.size #width*height
# Examine every pixel in im
for x in range(0,xs):
for y in range(0,ys):
#get the RGB color of the pixel
[r,g,b]=img[x,y]
get the RGB color of the pixel
[r,g,b]=img.getpixel((x, y))
update new rgb value
r = r + rtint
g = g + gtint
b = b + btint
value = (r,g,b)
assign new rgb value back to pixel
img.putpixel((x, y), value)
You can do everything you are wanting to do within PIL.
If you are wanting to reduce the value of every pixel by half, you can do something like:
import PIL
im = PIL.Image.open('input_filename.jpg')
im.point(lambda x: x * .5)
im.save('output_filename.jpg')
You can see more info about point operations here: https://pillow.readthedocs.io/en/latest/handbook/tutorial.html#point-operations
Additionally, you can do arbitrary pixel manipulation as:
im[row, col] = (r, g, b)
There are many ways to do this with Pillow. You can use Image.point, for example.
# Function to map over each channel (r, g, b) on each pixel in the image
def change_to_a_half(val):
return val // 2
im = Image.open('./imagefile.jpg')
im.point(change_to_a_half)
The function is actually only called 256 times (assuming 8-bits color depth), and the resulting map is then applied to the pixels. This is much faster than running a nested loop in python.
If you have Numpy and Matplotlib installed, one solution would be to convert your image to a numpy array and then e.g. save the image with matplotlib.
import matplotlib.pyplot as plt
import numpy as np
from PIL import Image
img = Image.open(jpg)
arr = np.array(img)
arr = arr/2 # divide each pixel in each channel by two
plt.imsave('output.png', arr.astype(np.uint8))
Be aware that you need to have a version of PIL >= 1.1.6

convert image (np.array) to binary image

Thank you for reading my question.
I am new to python and became interested in scipy. I am trying to figure out how I can make the image of the Racoon (in scipy misc) to a binary one (black, white). This is not taught in the scipy-lecture tutorial.
This is so far my code:
%matplotlib inline
import matplotlib.pyplot as plt
import numpy as np
from scipy import misc #here is how you get the racoon image
face = misc.face()
image = misc.face(gray=True)
plt.imshow(image, cmap=plt.cm.gray)
print image.shape
def binary_racoon(image, lowerthreshold, upperthreshold):
img = image.copy()
shape = np.shape(img)
for i in range(shape[1]):
for j in range(shape[0]):
if img[i,j] < lowerthreshold and img[i,j] > upperthreshold:
#then assign black to the pixel
else:
#then assign white to the pixel
return img
convertedpicture = binary_racoon(image, 80, 100)
plt.imshow(convertedpicture, cmap=plt.cm.gist_gray)
I have seen other people using OpenCV to make a picture binary, but I am wondering how I can do it in this way by looping over the pixels? I have no idea what value to give to the upper and lower threshold, so I made a guess of 80 and 100. Is there also a way to determine this?
In case anyone else is looking for a quick minimal example to experiment with, here's what I used to binarize an image:
from scipy.misc import imread, imsave
# read in image as 8 bit grayscale
img = imread('cat.jpg', mode='L')
# specify a threshold 0-255
threshold = 150
# make all pixels < threshold black
binarized = 1.0 * (img > threshold)
# save the binarized image
imsave('binarized.jpg', binarized)
Input:
Output:
You're overthinking this:
def to_binary(img, lower, upper):
return (lower < img) & (img < upper)
In numpy, the comparison operators apply over the whole array elementwise. Note that you have to use & instead of and to combine the booleans, since python does not allow numpy to overload and
You don't need to iterate over the x and y positions of the image array. Use the numpy array to check if the array is above of below the threshold of interest. Here is some code that produces a boolean (true/false) array as the black and white image.
# use 4 different thresholds
thresholds = [50,100,150,200]
# create a 2x2 image array
fig, ax_arr = plt.subplots(2,2)
# iterate over the thresholds and image axes
for ax, th in zip(ax_arr.ravel(), thresholds):
# bw is the black and white array with the same size and shape
# as the original array. the color map will interpret the 0.0 to 1.0
# float array as being either black or white.
bw = 1.0*(image > th)
ax.imshow(bw, cmap=plt.cm.gray)
ax.axis('off')
# remove some of the extra white space
fig.tight_layout(h_pad=-1.5, w_pad=-6.5)

Categories