I'm trying to invert the pixels of an RGB image. That is, simply subtracting the intensity value of each channel (red, green, blue) of each pixel from 255.
I have the following so far:
from PIL import Image
im = Image.open('xyz.png')
rgb_im = im.convert('RGB')
width, height = im.size
output_im = Image.new('RGB', (width,height))
for w in range(width):
for h in range(height):
r,g,b = rgb_im.getpixel((w,h))
output_r = 255 - r
output_g = 255 - g
output_b = 255 - b
output_im[w,h] = (output_r, output_g, output_b)
When I run the above script, I get the following error:
Traceback (most recent call last):
File "image_inverse.py", line 31, in <module>
output_im[w,h] = (output_r, output_g, output_b)
File "/usr/lib/python2.7/dist-packages/PIL/Image.py", line 528, in __getattr__
raise AttributeError(name)
AttributeError: __setitem__
How can I solve this issue?
Thanks.
I guess you can use a vectorized operation if the image is a numpy array
from PIL import Image
im = Image.open('xyz.png')
im = 255 - im
You can use img.putpixel to assign the r,g,b,a values at each pixel-
from PIL import Image
im = Image.open('xyz.png')
rgb_im = im.convert('RGB')
width, height = im.size
output_im = Image.new('RGB', (width,height))
for w in range(width):
for h in range(height):
r,g,b = rgb_im.getpixel((w,h))
output_r = 255 - r
output_g = 255 - g
output_b = 255 - b
alpha = 1
output_im.putpixel((w, h), (output_r, output_g, output_b, alpha))
Convert image to numpy array, and you can perform the operation on all 2-dimensional arrays in one line
from PIL import Image
import numpy as np
image = Image.open('my_image.png')
# Convert Image to numpy array
image_array = np.array(image)
print(image_array.shape)
# Prints something like: (1024, 1024, 4)
# So we have 4 two-dimensional arrays: R, G, B, and the alpha channel
# Do `255 - x` for every element in the first 3 two-dimensional arrays: R, G, B
# Keep the 4th array (alpha channel) untouched
image_array[:, :, :3] = 255 - image_array[:, :, :3]
# Convert numpy array back to Image
inverted_image = Image.fromarray(image_array)
inverted_image.save('inverted.png')
Related
trying to save an inverted image, saved inverted RGB colour data in array pixelArray, then converted this to a numpy array. Not sure what is wrong but any help is appreciated.
from PIL import Image
import numpy as np
img = Image.open('image.jpg')
pixels = img.load()
width, height = img.size
pixelArray = []
for y in range(height):
for x in range(width):
r, g, b = pixels[x, y]
pixelArray.append((255-r,255-b,255-g))
invertedImageArray = np.array(pixelArray, dtype=np.uint8)
invertedImage = Image.fromarray(invertedImageArray, 'RGB')
invertedImage.save('inverted-image.jpeg')
img.show()
getting error code "ValueError : not enough image data"
Your np.array creates an array shape (4000000, 3) instead of (2000, 2000, 3).
Also, you may find that directly mapping the subtraction to the NumPy array is faster and easier
from PIL import Image
import numpy as np
img = Image.open('image.jpg')
pixelArray = np.array(img)
pixelArray = 255 - pixelArray
invertedImageArray = np.array(pixelArray, dtype=np.uint8)
invertedImage = Image.fromarray(invertedImageArray, 'RGB')
invertedImage.save('inverted-image.jpeg')
PIL already provides an easier way to invert the image colours with the ImageOps module.
from PIL import Image, ImageOps
img = Image.open('image.jpg')
invertedImage = ImageOps.invert(img)
invertedImage.save('inverted-image.jpeg')
I have a grayscale numpy image (shape=(1024, 1024, 1), dtype=float) that I'm trying to translate into the same image, but with the grayscale values assigned to the red channel (ie. the same image but in redscale).
Here's the original image:
Which is generated using numpy:
def create_mandelbrot_matrix(width, height, max_iter=100):
X = np.linspace(-2, 1, width)
Y = np.linspace(-1, 1, height)
#broadcast X to a square array
C = X[:, None] + 1J * Y
#initial value is always zero
Z = np.zeros_like(C)
exit_times = max_iter * np.ones(C.shape, np.int32)
mask = exit_times > 0
for k in range(max_iter):
Z[mask] = Z[mask] * Z[mask] + C[mask]
mask, old_mask = abs(Z) < 2, mask
#use XOR to detect the area which has changed
exit_times[mask ^ old_mask] = k
return exit_times.T
def mandelbrot_image(width, height, max_iter=100):
mandelbrot_matrix = create_mandelbrot_matrix(width, height, max_iter)
img = np.expand_dims(mandelbrot_matrix, axis=2)
return img
This function results in a totally different image from the original:
def mandelbrot_red_image(w, h):
mandelbrot_img = mandelbrot_image(w, h)
print(mandelbrot_img.shape) # (1024, 1024, 1)
img = np.zeros((w, h, 3))
img[:, :, 0] = mandelbrot_img_int.reshape((w, h))
return img
I dont know how your mandelbrot_image works, but image shapes are usually (h, w), due to the number of lines in a matrix being the first dimension, and the height.
Another point is that, maybe your dtype is not 'uint8', I had to do a conversion in order to the image appear properly.
This code worked for me
from cv2 import cv2
import numpy as np
img = cv2.imread('./mandelbrot.png', cv2.IMREAD_GRAYSCALE)
h, w = img.shape
color_img = np.zeros([h, w, 3])
color_img[:, :, 2] = img # In opencv images are BGR
cv2.imshow('color_mandelbrot', color_img.astype('uint8'))
cv2.waitKey(0)
cv2.destroyAllWindows()
I have an image that is 384x384 pixels.I want to mask out or replace everything in the middle of the image, so I just have the borders of the image. For this I have created a mask that is 352x352 pixels (just a black image). Now I want to place this mask in the middle of the image so it has a distance of 32 pixels to every corner.
I thought of something like this but it does not work:
mask = cv2.imread('C://Users//but//Desktop//mask.png')
hh, ww, dd = mask.shape
image = cv2.imread('C://Users//but//Desktop//Train//OK//train_image1.jpg')
x = 32
y = 352
print('left:', x, 'top:', y)
# put mask into black background image
mask2 = np.full_like(image, 255)
mask2[x:x + hh, y:y + ww] = mask
# apply mask to image
img_masked = cv2.bitwise_and(image, mask2)
cv2.imshow('masked image', img_masked)
cv2.waitKey(0)
cv2.destroyAllWindows()
The error I get is:
Traceback (most recent call last):
File "C:/Users/but/PycharmProjects/SyntheticDataWriter.py", line 17, in <module>
mask2[x:x + hh, y:y + ww] = mask
ValueError: could not broadcast input array from shape (352,352,3) into shape (352,32,3)
You can make the middle of the image black without creating a black image and overlaying it or anding it, just use Numpy slicing:
import cv2
# Load image and get its dimensions
im = cv2.imread('paddington.png', cv2.IMREAD_COLOR)
h,w = im.shape[0:2]
# Make middle black
im[32:h-32, 32:w-32, :] = 0
cv2.imwrite('result.png', im)
Here's a thumbnail of how he used to be in case anyone doesn't know:
This will help you to add a mask to your image.
import cv2
# image = cv2.imared("path to image") # (384x384X3)
image = np.ones((384, 384, 3), dtype=np.uint8) * 150 # creating 3d image with pixel value 150
h, w, c = image.shape
border_padding = 32 # border width and height you want to set
# we will set pixel value to zero from [32: 384-32, 32: 384-32]
image[border_padding:h-border_padding, border_padding:w-border_padding] = 0 # black mask image
Image
I have an image with this dimension (1280 x 960). To create a Blank image with this dimension, I use this:
import cv2
import numpy as np
blank_image2 = 255 * np.ones(shape=[960, 1280, 3], dtype=np.uint8)
Is it possible to create a blank image based on the dimension of another image? Something like this:
import cv2
import numpy as np
blank_image2 = 255 * np.ones(shape=image, dtype=np.uint8)
You can use the shape of the image object:
image = cv2.imread('img.jpg')
h, w, c = image.shape
blank_image2 = 255 * np.ones(shape=(h, w, c), dtype=np.uint8)
Amin is correct, I'm just sharing an alternative using 'ones_like' - similar to Mark's suggestion:
image = cv2.imread('img.jpg')
blank_image = 255 * np.ones_like(image , dtype = np.uint8)
I try to convert a RGB image to grayscale using python as a function but the problem is I give it a RGB image that have height, width and channel but after the code I should have an image with just height and width but it gives me an image with height, width and channel why?
def RGBtoGRAY(img):
height, width, channels = img.shape
grayimg = img
for i in range(height):
for j in range(width):
grayimg[i,j] = 0.3 * image[i,j][0] + 0.59 * image[i,j][1] + 0.11 * image[i,j][2]
return grayimg
the size of the input image is
image.shape
(533, 541, 3)
the size of the output image is
grayimage.shape
(533, 541, 3)
normally I want to find in the size of the output image
(533, 541)
You should avoid using for loops when performing image processing since it is very slow. Instead you can use Numpy which is highly optimized for vector operations. Using this grayscale conversion formula:
gray = R * .299 + G * .587 + B * .114
Method #1: apply_along_axis:
import cv2
import numpy as np
def grayscale(colors):
r, g, b = colors
return 0.299 * r + 0.587 * g + 0.114 * b
# Create image of size 100x100 of random pixels
# Convert to grayscale
image = np.random.randint(255, size=(100,100,3),dtype=np.uint8)
gray = np.apply_along_axis(grayscale, 2, image)
# Display
cv2.imshow('image', image)
cv2.imshow('gray', gray)
cv2.waitKey()
Before -> After
Method #2: cv2.cvtColor
You could use OpenCV directly and read in the image as grayscale with cv2.imread by passing in the cv2.IMREAD_GRAYSCALE or 0 flag to load the image as grayscale.
image = cv2.imread('img.png', cv2.IMREAD_GRAYSCALE) # OR
# image = cv2.imread('img.png', 0)
If you already have the image loaded, you can convert the RGB or BGR image to grayscale using cv2.cvtColor
image = cv2.imread('img.png')
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
Assuming you are using a for loop, because you intent to solve it "manually" (like C code), there are number of issues with your implementation:
The assignment grayimg = img in Python does not create a copy of img (the result is that grayimg referencing img).
You meant to use: grayimg = img.copy().
img has 3 dimensions, so when using grayimg = img, grayimg also has 3 dimensions.
You need to create grayimg with two dimensions.
Example for creating grayimg and initialize to zeros:
grayimg = np.zeros((height, width), img.dtype)
Inside the for loop, you are using image instead of img.
Here is a corrected version of RGBtoGRAY:
def RGBtoGRAY(img):
height, width, channels = img.shape
#grayimg = img
# Create height x width array with same type of img, and initialize with zeros.
grayimg = np.zeros((height, width), img.dtype)
for i in range(height):
for j in range(width):
grayimg[i,j] = 0.3 * img[i,j][0] + 0.59 * img[i,j][1] + 0.11 * img[i,j][2]
return grayimg