Resizing RGB image with cv2 numpy and Python 2.7 - python

I want to resize an RGB image using Python 2.7. I tried using cv2.resize funcion, but it always returns a single channel image:
(Pdb) x = cv2.imread('image.jpg')
(Pdb) x.shape
(50, 50, 3)
(Pdb) x = cv2.resize(x, (40, 40))
(Pdb) x.shape
(40, 40)
I would like the final output of x.shape to be (40, 40, 3).
Is there a more pythonic way to resize the RGB image other than looping through the three channels and resizing each one separately?

Try this code:
import numpy as np
import cv2
image = cv2.imread('image.jpg')
cv2.imshow("Original", image)
"""
The ratio is r. The new image will
have a height of 50 pixels. To determine the ratio of the new
height to the old height, we divide 50 by the old height.
"""
r = 50.0 / image.shape[0]
dim = (int(image.shape[1] * r), 50)
resized = cv2.resize(image, dim, interpolation = cv2.INTER_AREA)
cv2.imshow("Resized (Height) ", resized)
cv2.waitKey(0)

Related

make all images' shape the same in python

I wanted to utilize some images using CNN classification.
however, the problem is the image shape is different for example
for i in range(1,len(x_train)):
print(print(x_train_resize[i].shape))
this shows the images' shapes for all the images that i am using this gives output of
None
(100, 100)
None
(100, 100)
None
(100, 100, 3)
None
(100, 100, 4)
as shown above, is there a way to make the shapes of the images that i have all the same as
(100, 100, 1) or (100, 100, 3)
(100, 100) means grayscale image.
(100, 100, 3) means RGB image.
(100, 100, 4) means RGBA image.
If you have numpy grayscale image img_gray with shape (100,100) then you can duplicate layers to create (100, 100, 3) like in RGB
img_rgb = np.dstack((img_gray, img_gray, img_gray))
If you add alpha layer with values 255 then you get (100, 100, 4) like in RGBA
alpha = np.ones((100, 100), dtype='uint8') * 255
img_rgba = np.dstack((img_rgb, alpha))
If you have img_rgba with (100, 100, 4) then you can skip alpha layer to get img_rgb
img_rgb = img_rgba[:,:,:3]
to convert rgb to grayscale you could calculate
img_gray = (img_rgb[:,:,0] + img_rgb[:,:,1] + img_rgb[:,:,2]) // 3
but better is formula GRAY = 0.2126 * R + 0.7152 * G + 0.0722 * B
img_gray = int(0.2126 * img_rgb[:,:,0] + 0.7152 * img_rgb[:,:,1] + 0.0722 * img_rgb[:,:,2])
Wikipedia: Converting colour to greyscale
If you use OpenCV which also use numpy arrays then it has function to convert colors.
gray to RGB
img_rgb = cv2.cvtColor(img_gray, cv2.COLOR_GRAY2RGB)
gray to RBGA
img_rgba = cv2.cvtColor(img_gray, cv2.COLOR_GRAY2RGBA)
RGB to RBGA
img_rgba = cv2.cvtColor(img_rgb, cv2.COLOR_RGB2RGBA)
and in other direction
RGB to gray
img_gray = cv2.cvtColor(img_rgb, cv2.COLOR_RGB2GRAY)
RBGA to gray
img_gray = cv2.cvtColor(img_rgba, cv2.COLOR_RGBA2GRAY)
RGBA to RBG
img_rgb = cv2.cvtColor(img_rgba, cv2.COLOR_RGBA2RGB)
You could also use pillow Image.convert but it needs to
convert numpy array to pillow Image - img = Image.fromarray(array),
convert color - img = img.convert(...),
convert back pillow Image to numpy array - array = np.asarray(img).
Doc: Image.fromarray()
EDIT:
Minimal working example
import numpy as np
img_gray = np.zeros((100, 100), dtype='uint8')
# create image with cross
for y in range(100):
img_gray[y,y] = int(255 * (y/100))
img_gray[y,99-y] = int(255 * (y/100))
print('img_gray.shape:', img_gray.shape) # (100, 100)
img_rgb = np.dstack((img_gray, img_gray, img_gray))
print('img_rgb.shape:', img_rgb.shape) # (100, 100, 3)
alpha = np.ones((100, 100), dtype='uint8') * 255
img_rgba = np.dstack((img_rgb, alpha))
print('img_rgba.shape:', img_rgba.shape)
import matplotlib.pyplot as plt
plt.imshow(img_gray)
plt.show()
plt.imshow(img_rgb)
plt.show()
plt.imshow(img_rgba)
plt.show()
# --- OpenCV ---
import cv2
img_cv2_rgb = cv2.cvtColor(img_gray, cv2.COLOR_GRAY2RGB)
print('img_cv2_rgb.shape:', img_cv2_rgb.shape)
img_cv2_rgba = cv2.cvtColor(img_gray, cv2.COLOR_GRAY2RGBA)
print('img_cv2_rgba.shape:', img_cv2_rgba.shape)
img_cv2_rgba2 = cv2.cvtColor(img_cv2_rgb, cv2.COLOR_RGB2RGBA)
print('img_cv2_rgba2.shape:', img_cv2_rgba2.shape)
cv2.imshow('gray', img_gray)
cv2.imshow('rgb', img_cv2_rgb)
cv2.imshow('rgba', img_cv2_rgba)
cv2.waitKey(0)
cv2.destroyAllWindows()

How to stack a transparent image on another image in opencv python

Wanted to have two images where i have a mask stacked on top of another image. But in doing so, i wish to not have two images blend together, rather have the final image stacked onto each other like layers
Here's my original images
masked image
code
import cv2
import numpy as np
image = cv2.imread('test72.jpg')
image2 = cv2.imread('test63.jpg')
blank = np.full((image.shape[0], image.shape[1], 3), (255,255,255), np.uint8)
circle = cv2.circle(blank, (300,300), 10, (0, 0, 0), thickness= 100)
blur = cv2.blur(circle, (50, 50), 0)
subtract = cv2.subtract(image, blur)
blended = cv2.addWeighted(image2, 1, subtract, 1, 0)
cv2.imwrite('mask.jpg', subtract)
cv2.imwrite('blend.jpg', blended)
cv2.waitKey(0)
cv2.destroyAllWindows()
Here is what the result looks like when the function cv2.addweighted is added, which results in the bannanas blending in the shoes, is there another function in OpenCv i could do to make this stack rather than blend?
Something like this might work in your case:
im1_alpha = blur/255
im2_alpha = (255-blur)/255
out_img = ((image2 * im1_alpha) + (image * im2_alpha)).astype(np.uint8)
plt.imshow(out_img)
Here is another answer!
I followed this tutorial right here! Alpha blending tutorial right here
import cv2
import numpy as np
image = cv2.imread('test72.jpg')
image2 = cv2.imread('test63.jpg')
blank = np.full((image.shape[0], image.shape[1], 3), (255,255,255), np.uint8)
circle = cv2.circle(blank, (300,350), 10, (0, 0, 0), thickness= 100)
blur = cv2.blur(circle, (50, 50), 0)
image = image.astype(float)
image2 = image2.astype(float)
alpha = blur.astype(float)/255
multiply = cv2.multiply(alpha, image2)
multiply2 = cv2.multiply(1.0 - alpha, image)
add = cv2.add(multiply, multiply2).astype(np.uint8)
cv2.imshow('alpha', add)
cv2.waitKey(0)
cv2.destroyAllWindows()

How to change dimensions of np.array for greyscale images

I have the following code to load an image:
img = imread(os.path.join('./Faces/','10.png'))
print(img.shape)
img = np.mean(img, axis=2)
img = img.astype(int)
print(img.shape)
The output of this code is as follows:
(200, 180, 3)
(200, 180)
I understand that I'm averaging out the RGB layers into a greyscale value, but I have my Keras input layer defined with shape (200, 280, 1). Is there a way to have the shape changed to this? Is there even a functional difference between having a matrix of the two shapes as outputted above?
You could use the expand_dims function in numpy (see documentation).
It works as follows in your case:
img = img.astype(int)
print(img.shape)
# Prints (100, 100)
img = np.expand_dims(img, axis=2)
print(img.shape)
# Prints (100, 100, 1)
You shouldn't average out the channels. There's a particular balance between the RGB channels to transform a picture to grayscale, and it's not conveniently 0.33% each. It's that:
((0.3*R) + (0.59*G) + (0.11*B))
Instead of averaging or doing it manually, I suggest that you use:
import cv2
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
Then add a dimension:
img = img[..., np.newaxis]
or
img = np.expand_dims(img, -1)
The functional difference is that obviously, your CNN will not see color if you turn it into grayscale. So it won't be able to use this information to classify.

BGR values of masked image (OpenCV, Python)

Using the follow image..
... I am applying this code to create a circle mask:
import cv2
import numpy as np
img = cv2.imread("car.png")
height, width, depth = img.shape
circle_img = np.zeros((height, width), np.uint8)
mask = cv2.circle(circle_img, (int(width / 2), int(height / 2)), 90, 1, thickness=-1)
masked_img = cv2.bitwise_and(img, img, mask=circle_img)
cv2.imshow("masked", masked_img)
cv2.waitKey(0)
This is the output..
How can I find BGR values of the circle using OpenCV ?
You can do it using numpy arrays.
circle_locations = mask == 1
bgr = img[circle_locations]
EDIT: I'm not sure if your mask has values in {0, 1} though I assume it does. If its background value is 0 and all positive values are forground, just change the == 1 to a > 1.

Create transparent image in opencv python

I am trying to make a transparent image and draw on it, and after I will addWeighted over the base image.
How can I initialize fully transparent image with width and hight in openCV python?
EDIT: I want to make a effect like in Photoshop, having stack of the layers, all stacked layers are initially transparent and drawing is performed on fully transparent layer. On the end I will merge all layers to get final image
For creating a transparent image you need a 4 channel matrix, 3 of which would represent RGB colors and the 4th channel would represent Alpha channel, To create a transparent image, you can ignore the RGB values and directly set the alpha channel to be 0. In Python OpenCV uses numpy to manipulate matrices, so a transparent image can be created as
import numpy as np
import cv2
img_height, img_width = 300, 300
n_channels = 4
transparent_img = np.zeros((img_height, img_width, n_channels), dtype=np.uint8)
# Save the image for visualization
cv2.imwrite("./transparent_img.png", transparent_img)
If you want to draw on several "layers" and then stack the drawings together, then how about this:
import cv2
import numpy as np
#create 3 separate BGRA images as our "layers"
layer1 = np.zeros((500, 500, 4))
layer2 = np.zeros((500, 500, 4))
layer3 = np.zeros((500, 500, 4))
#draw a red circle on the first "layer",
#a green rectangle on the second "layer",
#a blue line on the third "layer"
red_color = (0, 0, 255, 255)
green_color = (0, 255, 0, 255)
blue_color = (255, 0, 0, 255)
cv2.circle(layer1, (255, 255), 100, red_color, 5)
cv2.rectangle(layer2, (175, 175), (335, 335), green_color, 5)
cv2.line(layer3, (170, 170), (340, 340), blue_color, 5)
res = layer1[:] #copy the first layer into the resulting image
#copy only the pixels we were drawing on from the 2nd and 3rd layers
#(if you don't do this, the black background will also be copied)
cnd = layer2[:, :, 3] > 0
res[cnd] = layer2[cnd]
cnd = layer3[:, :, 3] > 0
res[cnd] = layer3[cnd]
cv2.imwrite("out.png", res)
To convert an image's white parts to transparent:
import cv2
import numpy as np
img = cv2.imread("image.png", cv2.IMREAD_UNCHANGED)
img[np.where(np.all(img[..., :3] == 255, -1))] = 0
cv2.imwrite("transparent.png", img)

Categories