Numpy create an empty alpha image - python

I wanted to create a blank alpha image to parse some data from py-opencv and save it on an transparent background png file.
I tried :
blank_image = np.zeros((H,W,4), np.uint8)
and
blank_image = np.full((H, W, 4) , (0, 0, 0, 0), np.uint8)
(H and W are Height and Width)
Both still render a black background instead of a transparent one.
how to get a blank alpha transparent image?
Thanks in advance :)
Edits:
as mentioned by Mark Setchell: you need to specify the alpha channel on other colors involved:
# size of the image
(H , W) = 1080, 1080
# Blank image with RGBA = (0, 0, 0, 0)
blank_image = np.full((H, W, 4), (0, 0, 0, 0), np.uint8)
# Green color with Alpha=255
RGBA_GREEN = (0, 255, 0, 255)
# Opencv element using the RGBA color
cv2.putText(blank_image, 'my opencv element', (20 , 20), cv2.FONT_HERSHEY_SIMPLEX, 0.5, RGBA_GREEN, 2)
cv2.imwrite('image_alpha.png', blank_image)

You need to make the alpha channel = 255 to see anything.
import numpy as np
H, W = 128, 256
blank_image = np.zeros((H,W,4), np.uint8)
# Make first 10 rows red and opaque
blank_image[:10] = [255,0,0,255]
# Make first 10 columns green and opaque
blank_image[:,:10] = [0,255,0,255]
You can also make your RGB image as you wish, then create an alpha layer entirely separately and add it afterwards:
# Make solid red image
RGB = np.full((H, W, 3) , (255, 0, 0), np.uint8)
# Make a gradient alpha channel, left-to-right, 0..255
alpha = np.repeat(np.arange(256,dtype=np.uint8)[np.newaxis,:], 128, axis=0)
# Apply alpha to RGB image to yield RGBA image
RGBA = np.dstack((RGB,alpha))

Related

Setting the alpha channel of an image, based on the pixel values in another image

I have two images. In one image all non-alpha channel pixels are equal to 0, and I'd like the alpha channel values to equal 255 where in the other image which is of equal size, the pixels are anything but 0. In this attempt, I'm attempting to create a 4 channel np array based off of the original image, and then use np.argwhere to find where the pixel valeus are non-zero, and then in the new np array, set the alpha channel value based on that.
For example, for each pixel in my input image with values [255, 255, 255], I'd like the corresponding pixel in my new image to be [0, 0, 0, 255]. For each pixel in my input image with values [0, 0, 0], I'd like the corresponding pixel in my new image to be [0, 0, 0, 0].
mask_file = cv.imread(r'PlateMask_0001.png', cv.IMREAD_UNCHANGED)
scale_factor = 0.125
w = int(mask_file.shape[1] * scale_factor)
h = int(mask_file.shape[0] * scale_factor)
scaled = cv.resize(mask_file, (w, h))
coords = np.argwhere(scaled > 0)
new_object = np.zeros((120, 160, 4))
new_object[coords, :] = 255
cv.imshow('Mask', mask)
cv.imshow('Scaled', new_object)
cv.waitKey(0)
cv.destroyAllWindows()
This is my first question on Stack so please feel free to suggest improvements on question formatting, etc. Thank you.
Consider img1 to be your original image and img2 to be the image where alpha channel needs to be modified.
In the following, the alpha channel of img2 contains value 255 in the coordinate where img1 has (255, 255, 255):
img2[:,:,3][img1 == (255, 255, 255)] = 255
Likewise for value 0:
img2[:,:,3][img1 == (0, 0, 0)] = 0

How to make black background in cv2.putText with Python OpenCV

I have a project of opencv where on the frame I am displaying some text using cv2.putText(). Currently it looks like below:
As you can see on the top left corner, the text is present but its not clearly visible. Is it possible to make background black so that the text will then appear good. Something like below image:
Even if the black background covers till right side of the frame, that is also fine. Below is the code I am using for putting text on frame:
cv2.putText(frame, "Data: N/A", (5, 30), cv2.FONT_HERSHEY_COMPLEX_SMALL, 1, (0, 0, 255), 1)
cv2.putText(frame, "Room: C1", (5, 60), cv2.FONT_HERSHEY_COMPLEX_SMALL, 1, (0, 0, 255), 1)
Is there any prebuilt method/library available in opencv which can do this. Can anyone please suggest a good way?
Use this function:
import cv2
def draw_text(img, text,
font=cv2.FONT_HERSHEY_PLAIN,
pos=(0, 0),
font_scale=3,
font_thickness=2,
text_color=(0, 255, 0),
text_color_bg=(0, 0, 0)
):
x, y = pos
text_size, _ = cv2.getTextSize(text, font, font_scale, font_thickness)
text_w, text_h = text_size
cv2.rectangle(img, pos, (x + text_w, y + text_h), text_color_bg, -1)
cv2.putText(img, text, (x, y + text_h + font_scale - 1), font, font_scale, text_color, font_thickness)
return text_size
Then you can invoke the function like this:
image = 127 * np.ones((100, 200, 3), dtype="uint8")
pos = (10, 10)
w, h = draw_text(image, "hello", pos=(10, 10))
draw_text(image, "world", font_scale=4, pos=(10, 20 + h), text_color_bg=(255, 0, 0))
cv2.imshow("image", image)
cv2.waitKey()
note that by default it paints a black background, but you can use a different color if you want.
There's no prebuilt method but a simple appraoch is to use cv2.rectangle + cv2.putText. All you need to do is to draw the black rectangle on the image followed by placing the text. You can adjust the x,y,w,h parameters depending on how large/small you want the rectangle. Here's an example:
Input image:
Result:
import cv2
import numpy as np
# Load image, define rectangle bounds
image = cv2.imread('1.jpg')
x,y,w,h = 0,0,175,75
# Draw black background rectangle
cv2.rectangle(image, (x, x), (x + w, y + h), (0,0,0), -1)
# Add text
cv2.putText(image, "THICC flower", (x + int(w/10),y + int(h/2)), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (255,255,255), 2)
# Display
cv2.imshow('image', image)
cv2.waitKey()
Here is one way to do that in Python OpenCV.
Read the input
Create an image of your desired background color that is the same size as the input
Draw your text on the background image
Get the bounding rectangle for the text region
Copy the text region from the background color image to a copy of the input image
Save the results
Input:
import cv2
import numpy as np
# load image
img = cv2.imread("zelda1.jpg")
# create same size image of background color
bg_color = (0,0,0)
bg = np.full((img.shape), bg_color, dtype=np.uint8)
# draw text on bg
text_color = (0,0,255)
cv2.putText(bg, "Data: N/A", (5,30), cv2.FONT_HERSHEY_COMPLEX_SMALL, 0.75, text_color, 1)
# get bounding box
# use channel corresponding to color so that text is white on black background
x,y,w,h = cv2.boundingRect(bg[:,:,2])
print(x,y,w,h)
# copy bounding box region from bg to img
result = img.copy()
result[y:y+h, x:x+w] = bg[y:y+h, x:x+w]
# write result to disk
cv2.imwrite("zelda1_background_text.jpg", bg)
cv2.imwrite("zelda1_text.jpg", result)
# display results
cv2.imshow("TEXT", bg)
cv2.imshow("RESULT", result)
cv2.waitKey(0)
cv2.destroyAllWindows()
Text on background color image:
Text on input image:
P.S. You can adjust the bounding rectangle (x,y,w,h) values to add some padding if you want when you do the crop.
import cv2 \
import numpy as np
#### Load image, define rectangle bounds
image = cv2.imread(r'C:\Users\Bharath\Downloads\test.jpg')
#### overlay space
x,y,w,h = 40,30,300,60
#### alpha, the 4th channel of the image
alpha = 0.3
overlay = image.copy()
output = image.copy()
##### corner
cv2.rectangle(overlay, (x, x), (x + w, y + h), (0,0,0), -1)
##### putText
cv2.putText(overlay, "HELLO WORLD..!", (x + int(w/10),y + int(h/1.5)), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (255,255,255), 2)
#### apply the overlay
cv2.addWeighted(overlay, alpha, output, 1 - alpha,0, output)
##### Display
cv2.imshow("Output", output)\
cv2.waitKey(0)
`
input
output

How do I make an inverse filled transparent rectangle with OpenCV?

I want to make an inverse filled rectangle in this picture.
The code I have:
import cv2
lena = cv2.imread('lena.png')
output = lena.copy()
cv2.rectangle(lena, (100, 100), (200, 200), (0, 0, 255), -1)
cv2.addWeighted(lena, 0.5, output, 1 - .5, 0, output)
cv2.imshow('', output)
What I want:
Here's what I would do:
# initialize output
output = np.zeros_like(lena, dtype=np.uint8)
output[:,:,-1] = 255
# this is your box top_x
tx,ly,bx,ry = 100,100,200,200
# copy lena to output
output[tx:bx,ly:ry] = lena[tx:bx,ly:ry]
cv2.addWeighted(lena, 0.5, output, 1 - .5, 0, output);
OUtput:
Here is another way to do it in Python/OpenCV. Though it is not as elegant as the solution from Quang Hoang.
Read the input
Create a red image of the same size
Blend the red image with the input
Create a white image with a black rectangle for the "hole"
Combine the blended image and the original image using the mask
Save the result
Input:
import cv2
import numpy as np
# read image
img = cv2.imread('lena.jpg')
# create red image
red = np.full_like(img,(0,0,255))
# add red to img and save as new image
blend = 0.5
img_red = cv2.addWeighted(img, blend, red, 1-blend, 0)
# create white image for mask base
mask = np.full_like(img, (1,1,1), dtype=np.float32)
# define rectangle for "hole" and draw as black filled on the white base mask
x1,y1,x2,y2 = 100,100,200,200
mask = cv2.rectangle(mask, (x1, y1), (x2, y2), (0, 0, 0), -1)
# combine img and img_red using mask
result = cv2.add(img*(1-mask),img_red*mask).astype(np.uint8)
cv2.imshow('img', img)
cv2.imshow('red', red)
cv2.imshow('img_red', img_red)
cv2.imshow('mask', mask)
cv2.imshow('result', result)
cv2.waitKey(0)
cv2.destroyAllWindows()
# save results
cv2.imwrite('lena_hole_mask.jpg', (255*mask).astype(np.uint8))
cv2.imwrite('lena_plus_red.jpg', result)
Mask:
Result:

Python-Imaging-Library only paste imageparts with 255 alpha

I want to use paste of the python PIL library to paste a image to a black background.
I know I can use the image itself as a alpha mask, but I only want to have the parts of the image where the alpha value is 255.
How is this possible?
Here is my code so far:
import PIL
from PIL import Image
img = Image.open('in.png')
background = Image.new('RGBA', (825, 1125), (0, 0, 0, 255))
offset = (50, 50)
background.paste(img, offset, img) #image as alpha mask as third param
background.save('out.png')
I can't find anything in the official but bad documentation
If I understand your question correctly, then
this is a possible solution. It generates
a dedicated mask, which is used for the paste:
from PIL import Image
img = Image.open('in.png')
# Extract alpha band from img
mask = img.split()[-1]
width, height = mask.size
# Iterate through alpha pixels,
# perform desired conversion
pixels = mask.load()
for x in range(0, width):
for y in range(0, height):
if pixels[x,y] < 255:
pixels[x,y] = 0
# Paste image with converted alpha mask
background = Image.new('RGBA', (825, 1125), (0, 0, 0, 255))
background.paste(img, (50, 50), mask)
background.save('out.png')
As a note, the alpha channel of the background image is fairly useless.
If you don't need it later on, you could also load the background with:
background = Image.new('RGB', (825, 1125), (0, 0, 0))

With the Python Imaging Library (PIL), how does one compose an image with an alpha channel over another image?

I have two images, both with alpha channels. I want to put one image over the other, resulting in a new image with an alpha channel, just as would occur if they were rendered in layers. I would like to do this with the Python Imaging Library, but recommendations in other systems would be fantastic, even the raw math would be a boon; I could use NumPy.
This appears to do the trick:
from PIL import Image
bottom = Image.open("a.png")
top = Image.open("b.png")
r, g, b, a = top.split()
top = Image.merge("RGB", (r, g, b))
mask = Image.merge("L", (a,))
bottom.paste(top, (0, 0), mask)
bottom.save("over.png")
Pillow 2.0 now contains an alpha_composite function that does this.
img3 = Image.alpha_composite(img1, img2)
I couldn't find an alpha composite function in PIL, so here is my attempt at implementing it with numpy:
import numpy as np
from PIL import Image
def alpha_composite(src, dst):
'''
Return the alpha composite of src and dst.
Parameters:
src -- PIL RGBA Image object
dst -- PIL RGBA Image object
The algorithm comes from http://en.wikipedia.org/wiki/Alpha_compositing
'''
# http://stackoverflow.com/a/3375291/190597
# http://stackoverflow.com/a/9166671/190597
src = np.asarray(src)
dst = np.asarray(dst)
out = np.empty(src.shape, dtype = 'float')
alpha = np.index_exp[:, :, 3:]
rgb = np.index_exp[:, :, :3]
src_a = src[alpha]/255.0
dst_a = dst[alpha]/255.0
out[alpha] = src_a+dst_a*(1-src_a)
old_setting = np.seterr(invalid = 'ignore')
out[rgb] = (src[rgb]*src_a + dst[rgb]*dst_a*(1-src_a))/out[alpha]
np.seterr(**old_setting)
out[alpha] *= 255
np.clip(out,0,255)
# astype('uint8') maps np.nan (and np.inf) to 0
out = out.astype('uint8')
out = Image.fromarray(out, 'RGBA')
return out
For example given these two images,
img1 = Image.new('RGBA', size = (100, 100), color = (255, 0, 0, 255))
draw = ImageDraw.Draw(img1)
draw.rectangle((33, 0, 66, 100), fill = (255, 0, 0, 128))
draw.rectangle((67, 0, 100, 100), fill = (255, 0, 0, 0))
img1.save('/tmp/img1.png')
img2 = Image.new('RGBA', size = (100, 100), color = (0, 255, 0, 255))
draw = ImageDraw.Draw(img2)
draw.rectangle((0, 33, 100, 66), fill = (0, 255, 0, 128))
draw.rectangle((0, 67, 100, 100), fill = (0, 255, 0, 0))
img2.save('/tmp/img2.png')
alpha_composite produces:
img3 = alpha_composite(img1, img2)
img3.save('/tmp/img3.png')

Categories

Resources