inverting image in Python with OpenCV - python

I want to load a color image, convert it to grayscale, and then invert the data in the file.
What I need: to iterate over the array in OpenCV and change every single value with this formula (it might be wrong but it seems reasonable for me):
img[x,y] = abs(img[x,y] - 255)
but I don't understand why doesn't it works:
def inverte(imagem, name):
imagem = abs(imagem - 255)
cv2.imwrite(name, imagem)
def inverte2(imagem, name):
for x in np.nditer(imagem, op_flags=['readwrite']):
x = abs(x - 255)
cv2.imwrite(name, imagem)
if __name__ == '__main__':
nome = str(sys.argv[1])
image = cv2.imread(nome)
gs_imagem = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
inverte(gs_imagem, "invertida.png")
inverte2(gs_imagem, "invertida2.png")
I don't want to do an explicit loop (I am trying to be more pythonic). I can see that in one image that got a white background it turned black, but only this it doesn't looks like the other colors are having much (if any) change.

You almost did it. You were tricked by the fact that abs(imagem-255) will give a wrong result since your dtype is an unsigned integer. You have to do (255-imagem) in order to keep the integers unsigned:
def inverte(imagem, name):
imagem = (255-imagem)
cv2.imwrite(name, imagem)
You can also invert the image using the bitwise_not function of OpenCV:
imagem = cv2.bitwise_not(imagem)

Alternatively, you could invert the image using the bitwise_not function of OpenCV:
imagem = cv2.bitwise_not(imagem)
I liked this example.

You can use "tilde" operator to do it:
import cv2
image = cv2.imread("img.png")
image = ~image
cv2.imwrite("img_inv.png",image)
This is because the "tilde" operator (also known as unary operator) works doing a complement dependent on the type of object
for example for integers, its formula is:
x + (~x) = -1
but in this case, opencv use an "uint8 numpy array object" for its images so its range is from 0 to 255
so if we apply this operator to an "uint8 numpy array object" like this:
import numpy as np
x1 = np.array([25,255,10], np.uint8) #for example
x2 = ~x1
print (x2)
we will have as a result:
[230 0 245]
because its formula is:
x2 = 255 - x1
and that is exactly what we want to do to solve the problem.

You can also do it with numpy.
import cv2
import numpy as np
image = cv2.imread('your_image', 0)
inverted = np.invert(image)
cv2.imwrite('inverted.jpg', inverted)

In Python/OpenCV, I think you want:
img = cv2.absDiff(img, 255)

Why not use the first line in the question with numpy?
inverted = np.abs(image - 255)
Just as simple as that. No iteration or any other function needed. numpy does that automatically for us :)

def invert(self, image=None):
if image is None:
image = self.image
image = image.astype("uint8") # --> pay attention to the image type
inverted = np.invert(image)
(thresh, im_bw) = cv2.threshold(inverted, 0, 1, cv2.THRESH_BINARY | cv2.THRESH_OTSU)
return im_bw

Related

Error: only size-1 arrays can be converted to Python scalars when blending and pasting an image on other Image

I was getting the error on the line blk_img[y_offset:y_end,x_offset:x_end] = int(resz_PerspctvTransformed).
I am trying to paste the image resz_PerspctvTransformed on a black background. Trying to implement the code below:
import cv2
import numpy as np
counter = 0
cordnts = np.zeros((4,2),dtype=int)
# Function definition
def mouseClickFun(event,x,y,flag,params):
global counter
if event == cv2.EVENT_LBUTTONDOWN:
if counter<=3 :
cordnts[counter]= x,y
counter=counter+1
img1 = cv2.imread('D:\\Project Code\\Input_img\\img_2.jpg',1)
print('Original Dimensions: ',img1.shape)
resz_image = cv2.resize(img1,None, fx=0.3, fy=0.3, interpolation= cv2.INTER_AREA)
print('Resized Dimenstion',resz_image.shape)
width,height= 400, 600
wBG,hBG=600,600
blk_img=np.zeros((wBG,hBG,3),dtype=int)
while True:
for x in range(0,4):
cv2.circle(resz_image,(cordnts[x,0],cordnts[x,1]),2,[0,255,255],2)
cv2.imshow("Resized_Image",resz_image)
cv2.setMouseCallback("Resized_Image",mouseClickFun)
basePnt=width/3
x_offset = int((wBG/4))
y_offset = 0 #Chnged to 0 from (wBG/4)
x_end = int((x_offset + ((wBG/4)*2)))
y_end = int((y_offset + ((hBG/4)*2)))
if counter == 4:
pnt1 = np.float32([cordnts[0],cordnts[1],cordnts[2],cordnts[3]])
pnt2 = np.float32([[0,0], [width,0], [basePnt,height], [(basePnt*2),height]])
PerspectiveMatrix= cv2.getPerspectiveTransform(pnt1,pnt2)
img_PerspctvTransformed= cv2.warpPerspective(resz_image,PerspectiveMatrix,(width,height))
resz_PerspctvTransformed = cv2.resize(img_PerspctvTransformed,(300,300), interpolation= cv2.INTER_AREA)
cv2.imshow("resized Img",resz_PerspctvTransformed)
blk_img[y_offset:y_end,x_offset:x_end] = int(resz_PerspctvTransformed)
cv2.imshow("Background Img",blk_img)
cv2.waitKey(0)
break
cv2.waitKey(1)
cv2.destroyAllWindows()
There are two mistakes in your code.
First, whenever you create an image or set an image type, make sure it is the correct type. You have created the image blk_img of type int that is of 32-bit size whereas an image can have integer values in the range of 0-255, that is, the image should be of the type np.uint8 (unsigned int 8). Thus, correct line 22 as:
blk_img=np.zeros((wBG,hBG,3),dtype=np.uint8)
Second of all, if you want to convert the image to an integer type, int(image_name) is not the correct syntax. Instead, use the code below:
blk_img[y_offset:y_end,x_offset:x_end] = np.asarray(resz_PerspctvTransformed, dtype=np.uint8)
Moreover, you do not need to worry about the data type of the resz_PerspctvTransformed image as it is already integer type.

Can I use cv2 or Numpy to Invert this picture?

Can I use cv2 or numpy to turn an image into a negative? Something like below but I need to edit still.
My question is mainly the top bit of code if I can use that to invert the grayscale and black&white both to a negative?
import cv2
import numpy as np
img = cv2.imageread('imagename.jpg')
print(img.dtype)
image_neg = 255 - img
cv2.imshow('negative',image_neg)
cv2.waitKey(0)
#######################################
from images import Image
def invert(image):
def blackAndWhite(image):
blackPixel = (0, 0, 0)
whitePixel = (255, 255, 255)
for y in range(image.getHeight()):
for x in range(image.getWidth()):
(r, g, b) = image.getPixel(x, y)
average = (r + g + b) // 3
if average < 128:
image.setPixel(x, y, blackPixel)
else:
image.setPixel(x, y, whitePixel)
def grayscale(image):
for y in range(image.getHeight()):
for x in range(image.getWidth()):
(r, g, b) = image.getPixel(x, y)
r = int(r * 0.299)
g = int(g * 0.587)
b = int(b * 0.114)
lum = r + g + b
image.setPixel(x, y, (lum, lum, lum))
def main():
filename = input("Enter the image file name: ")
image = Image(filename)
#Invert image
invert(image)
image.draw()
#Covert to greyscale, then invert
"""grayscale(image)
invert(image)
image.draw()"""
#Convert to black and white, then invert
"""blackAndWhite(image)
invert(image)
image.draw()"""
if __name__ == "__main__":
main()
I receive the following error:
Traceback (most recent call last):
File "invert.py", line 14, in <module>
image_neg = 255 - image
NameError: name 'image' is not defined
I changed the code in the beginning to say this:
import cv2
import numpy as np
image = cv2.imageread('smokey.gif')
print(image.dtype)
image_neg = 255 - image
cv2.imshow('negative',image_neg)
cv2.waitKey(0)
Well I thought this would work but it tells me line - "invertedImage = cv2.bitwise_not(imageToInvert)" has a SyntaxError: invalid non-printable character U+00A0
I edited my code correctly on here (4 spaces) and I have no clue why it's not showing correctly still.
from images import Image
import cv2
def invert(image):
imageToInvert = cv2.imread(filepath)
invertedImage = cv2.bitwise_not(imageToInvert)
cv2.imgwrite("BWimage.png",invertedImage)
print("invertedĀ imageĀ saved")
File_path='smokey.gif'
invert(File_path)
Not sure what error you are getting. Maybe something here will help?
Syntax: cv2.cv.flip(src, flipCode[, dst] )
Parameters:
src: Input array.
dst: Output array of the same size and type as src.
flip code: A flag to specify how to flip the array; 0 means flipping around the x-axis and positive value (for example, 1) means flipping around y-axis. Negative value (for example, -1) means flipping around both axes.
Return Value: It returns an image.
As found in OpenCV
example code:
# Python program to explain cv2.flip() method
# importing cv2
import cv2
# path
path = r'C:\Users\user\Desktop\geeks14.png'
# Reading an image in default mode
src = cv2.imread(path)
# Window name in which image is displayed
window_name = 'Image'
# Using cv2.flip() method
# Use Flip code 0 to flip vertically
image = cv2.flip(src, 0)
# Displaying the image
cv2.imshow(window_name, image)
cv2.waitKey(0)

Python pixel manipulation returning grey image instead of inverted

I'm doing some prototype code for a project. I'm using Pillow to open the image and other minor things, but I want to invert the image manually using its pixel values. I used two's complement in hopes of inverting it. However, when I display the final image, it's a solid grey square instead of inverted colors. I just used a picture of a possum, 275 pixels by 183 pixels. Any idea why it displays grey block and not an inverted image?
#importing Image module
from PIL import Image
import numpy
#sets numpy to print out full array
numpy.set_printoptions(threshold=numpy.inf)
def twos_comp(val, bits):
"""compute the 2's complement of int value val"""
if (val & (1 << (bits - 1))) != 0: # if sign bit is set e.g., 8bit: 128-255
val = val - (1 << bits) # compute negative value
return val
im = Image.open('possum.jpg')
#im.show()
data = numpy.asarray(im)
#print(data)
#print("FINISHED PRINTING")
#print('NOW PRINTING BINARY')
data_binary = numpy.unpackbits(data)
data_binary.ravel()
#print(data_binary)
#print('FINISHED PRINTING')
#getting string of binary array
binaryString = numpy.array2string(data_binary)
binaryString = ''.join(binaryString.split())
binaryString = binaryString[:-1]
binaryString = binaryString[1:]
#print("Binary String: " + binaryString)
out = twos_comp(int(binaryString,2), len(binaryString))
#print('Now printing twos:')
#print(out)
#formatting non-binary two's comp as binary
outBinary = "{0:b}".format(out)
#print('Now printing binary twos: ' + outBinary)
outBinary = outBinary.encode('utf-8')
a_pil_image = Image.frombytes('RGB', (275, 183), outBinary)
a_pil_image.show()
You can invert quite simply with PIL/Pillow:
from PIL import Image, ImageChops
# Load image from disk and ensure RGB
im = Image.open('lena.png').convert('RGB')
# Invert image and save to disk
res = ImageChops.invert(im)
res.save('result.png')
Turns Lena into negated Lena:
Or, if you want to be more mathematical about it:
from PIL import Image
import numpy as np
im = Image.open('lena.png').convert('RGB')
# Make Numpy array
imnp = np.array(im)
# Invert
imnp = 255 - imnp
# Save
Image.fromarray(imnp).save('result.png')
If you imagine a black image is represented by (0,0,0) and a white image by (255,255,255), it is hopefully not hard to see that inversion of colours is achieved by subtracting from 255 rather than using two's complement.

replace mask with original image opencv Python

I am trying to replace objects which I found using a mask with the original images pixels. I have a mask that shows black where the object is not detected and white if detected. I am then using the image in a where statement
image[np.where((image2 == [255,255,255].any(axis = 2))
I am stuck here and I have no idea how to change found white values to what the original image is (to use alongside other masks). I have tried image.shape and this did not work.
Thanks.
Make a copy of the mask and then draw the original image over the white pixels of the mask from the white pixel coordinates. You can also check mask == 255 to compare element-wise. You don't need np.where because you can index arrays via the boolean mask created by mask == 255.
out = mask.copy()
out[mask == 255] = original_image[mask == 255]
You can use bitwise operations. Try this:
replaced_image = cv2.bitwise_and(original_image,original_image,mask = your_mask)
Visit https://docs.opencv.org/3.3.0/d0/d86/tutorial_py_image_arithmetics.html
to learn more about bitwise operations
import os
import cv2
from netpbmfile import imread
img_dir = '.'
mask_dir = '.'
new_bg = 'image.png'
def get_foreground(fg_image_name, mask_name, bg_image_name):
fg_image = cv2.imread(fg_image_name)
mask = imread(mask_name)
mask_inverse = (1-mask)
bg_image = cv2.imread(bg_image_name)
bg_image = cv2.resize(bg_image, (fg_image.shape[1], fg_image.shape[0]))
foregound = cv2.bitwise_and(fg_image, fg_image, mask=mask)
background = cv2.bitwise_and(bg_image, bg_image, mask=mask_inverse)
composite = foregound + background
return composite
image_fg = get_foreground(os.path.join(img_dir, "NP1_0.jpg"), os.path.join(mask_dir, "NP1_0_mask.pbm"), new_bg)
cv2.imwrite("foreground.jpg", image_fg)

How to invert colors of image with PIL (Python-Imaging)?

I need to convert series of images drawn as white on black background letters to images where white and black are inverted (as negative). How can I achieve this using PIL?
Try the following from the docs: https://pillow.readthedocs.io/en/stable/reference/ImageOps.html
from PIL import Image
import PIL.ImageOps
image = Image.open('your_image.png')
inverted_image = PIL.ImageOps.invert(image)
inverted_image.save('new_name.png')
Note: "The ImageOps module contains a number of 'ready-made' image processing operations. This module is somewhat experimental, and most operators only work on L and RGB images."
If the image is RGBA transparent this will fail... This should work though:
from PIL import Image
import PIL.ImageOps
image = Image.open('your_image.png')
if image.mode == 'RGBA':
r,g,b,a = image.split()
rgb_image = Image.merge('RGB', (r,g,b))
inverted_image = PIL.ImageOps.invert(rgb_image)
r2,g2,b2 = inverted_image.split()
final_transparent_image = Image.merge('RGBA', (r2,g2,b2,a))
final_transparent_image.save('new_file.png')
else:
inverted_image = PIL.ImageOps.invert(image)
inverted_image.save('new_name.png')
For anyone working with an image in "1" mode (i.e., 1-bit pixels, black and white, stored with one pixel per byte -- see docs), you need to convert it into "L" mode before calling PIL.ImageOps.invert.
Thus:
im = im.convert('L')
im = ImageOps.invert(im)
im = im.convert('1')
now ImageOps must be:
PIL.ImageChops.invert(PIL.Image.open(imagepath))
note that this works for me in python 3.8.5
In case someone is inverting a CMYK image, the current implementations of PIL and Pillow don't seem to support this and throw an error. You can, however, easily circumvent this problem by inverting your image's individual bands using this handy function (essentially an extension of Greg Sadetsky's post above):
def CMYKInvert(img) :
return Image.merge(img.mode, [ImageOps.invert(b.convert('L')) for b in img.split()])
Of course ImageOps does its job well, but unfortunately it can't work with some modes like 'RGBA'. This code will solve this problem.
def invert(image: Image.Image) -> Image.Image:
drawer = ImageDraw.Draw(image)
pixels = image.load()
for x in range(image.size[0]):
for y in range(image.size[1]):
data = pixels[x, y]
if data != (0, 0, 0, 0) and isinstance(data, tuple):
drawer.point((x, y), (255 - data[0], 255 - data[1], 255 - data[2], data[3]))
return image
from PIL import Image
img = Image.open("archive.extension")
pixels = img.load()
for i in range(img.size[0]):
for j in range(img.size[1]):
x,y,z = pixels[i,j][0],pixels[i,j][1],pixels[i,j][2]
x,y,z = abs(x-255), abs(y-255), abs(z-255)
pixels[i,j] = (x,y,z)
img.show()
`

Categories