i have two images, i need to use numpy and opencv to overlay foreground on top of background using numpy masks.
import numpy as np
import cv2
import matplotlib.pyplot as plt
background = cv2.imread("background.jpg")
foreground = cv2.imread("foreground.png")
foreground = cv2.cvtColor(foreground ,cv2.COLOR_BGR2GRAY)
background = cv2.cvtColor(background ,cv2.COLOR_BGR2GRAY)
arr = []
for i in range(foreground.shape[1]): #parse forground pixels
temp_row = []
for j in range(foreground.shape[0]):
if((foreground [i][j] == 0)):#if pixel transperant draw background
temp_row.append(background[i][j])
else: # draw forground
temp_row.append(foreground[i][j])
arr.append(temp_row)
res_im = np.array(arr)
plt.figure()
plt.imshow(res_im, cmap='gray', vmin=0, vmax=255)
plt.show()
i used this solution but was told i needed to use masks.. help?
Here is one way to do that in Python/Opencv.
Read the transparent foreground image
Read the background image
Extract the alpha channel from the foreground image
Extract the BGR channels from the foreground image
Composite them together using np.where conditional
Save the result
Front:
Back:
import cv2
import numpy as np
# read foreground image
img = cv2.imread('front.png', cv2.IMREAD_UNCHANGED)
# read background image
back = cv2.imread('back.png')
# extract alpha channel from foreground image as mask and make 3 channels
alpha = img[:,:,3]
alpha = cv2.merge([alpha,alpha,alpha])
# extract bgr channels from foreground image
front = img[:,:,0:3]
# blend the two images using the alpha channel as controlling mask
result = np.where(alpha==(0,0,0), back, front)
# save result
cv2.imwrite("front_back.png", result)
# show result
cv2.imshow("RESULT", result)
cv2.waitKey(0)
Result:
Related
I have an image like this:
The image is transparent. I want to apply a red overlay just on the letters of the logo. I have experience with cv2.addWeight(...) but I always work on that using cv2.rectangle(...). Is there a way I can add a red overlay just on the shape of the image?
As you didn't answer my question in the comments, I am guessing what you want from the answer you have accepted.
You can do what you appear to want by simply adding a solid red overlay with weighting to the BGR channels only, without touching the alpha channel like this:
import cv2
import numpy as np
# Load image including alpha channel
img = cv2.imread('google.png', cv2.IMREAD_UNCHANGED)
# Make a solid red overlay same height and width as original but without alpha channel
redOverlay = np.full_like(img[:,:,0:3], (0,0,255))
# Add overlay to BGR channels leaving alpha unaffected and save result
img[..., 0:3] = cv2.addWeighted(img[..., 0:3], 0.7, redOverlay, 0.3, 0)
cv2.imwrite('result.png', img)
The alpha channel controls the transparent areas. So copying the alpha channel to the red channel should give a red overlay on the non-transparent areas. The IMREAD_UNCHANGED flag allows the reading of the signal without OpenCV removing the alpha (fourth channel).
import cv2
import numpy as np;
img = cv2.imread('test.png', cv2.IMREAD_UNCHANGED)
alpha = img[:,:,3];
imgz = np.zeros(img.shape, dtype=np.uint8)
imgz[:,:,2] = alpha;
imgz[:,:,3] = alpha;
dst = cv2.addWeighted(img, 0.7, imgz, 0.3, 0)
cv2.imwrite('test2.png', dst);
cv2.imshow("original", img)
cv2.imshow("alhpa", alpha)
cv2.imshow("final", dst)
cv2.waitKey(0)
I have a image and I want to do HE or CLAHE on specific area of the image.
I already have a mask for the image.
Is there any possible way to do so?
Here is the code to achieve that :
import cv2 as cv
import numpy as np
# Load your color image
#src = cv.imread("___YourImagePath__.jpg",
#cv.IMREAD_COLOR)
#Create random color image
src = np.random.randint(255, size=(800,800,3),dtype=np.uint8)
cv.imshow('Random Color Image',src)
cv.waitKey(0)
# conver to gray
gray = cv.cvtColor(src, cv.COLOR_BGR2GRAY)
# process gray image
equalized = cv.equalizeHist(gray)
# create a mask (binary image with same size as source image )
height,width,depth = src.shape
mask = np.zeros((height,width))
cv.circle(mask,( int(width/2),int(height/2)),int(width/3),1,thickness=-1)
# display mask
cv.imshow('Mask',mask)
cv.waitKey(0)
# Copy processed region using the mask
ProcessedRegion = np.where(mask!=0,equalized,gray)
#display result
cv.imshow('Processed region result', ProcessedRegion)
cv.waitKey(0)
Output :
To do so you need to perform the operation on the pixel intensities of the image which fall within the mask. For that these intensities must be stored separately.
Procedure:
Get the pixel locations of those in white (255), within the mask.
Pick intensity values (0 - 255) from the gray image present in these locations.
Perform your operation (CLAHE or HE) on these intensities. The result is a different collection of intensities.
Place these new intensity values in the collected locations.
Sample:
Input image:
Mask image:
Code:
import cv2
import numpy as np
# read sample image, convert to grayscale
img = cv2.imread('flower.jpg')
gray_img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# read mask image as binary image
mask = cv2.imread('flower_mask.jpg', 0)
# Step 1: store locations with value 255 (white)
loc = np.where(mask == 255)
# Step 2: Pick intensity values in these locations from the grayscale image:
values = gray[loc]
# Step 3: Histogram equalization on these values:
enhanced_values = cv2.equalizeHist(values)
# Step 4: Store these enhanced values in those locations:
gray2 = gray_img.copy()
for i, coord in enumerate(zip(loc[0], loc[1])):
gray2[coord[0], coord[1]] = enhanced_values[i][0]
cv2.imshow('Enhanced image', gray2)
Enhance image:
Grayscale image:
I implemented an algorithm that detects faces and I want to blur faces. I'm using PIL for blurring it.
image = Image.open(path_img)
draw = ImageDraw.Draw(image)
draw.ellipse((top, left, bottom, right), fill = 'white', outline ='white')
I got this with my code
Face to blur
I would like to use :
blurred_image = cropped_image.filter(ImageFilter.GaussianBlur(radius=10 ))
But I can't use it because I'm using an ImageDraw and it works only with Image class. How can I blur with an ellipse (circular) the face?
Thank you
blur the ellipse is the best way
With Pillow, the best way to do this is to use the blurred ellipse as a blending mask for composite.
from PIL import Image, ImageDraw, ImageFilter
def make_ellipse_mask(size, x0, y0, x1, y1, blur_radius):
img = Image.new("L", size, color=0)
draw = ImageDraw.Draw(img)
draw.ellipse((x0, y0, x1, y1), fill=255)
return img.filter(ImageFilter.GaussianBlur(radius=blur_radius))
kitten_image = Image.open("kitten.jpg")
overlay_image = Image.new("RGB", kitten_image.size, color="orange") # This could be a bitmap fill too, but let's just make it orange
mask_image = make_ellipse_mask(kitten_image.size, 150, 70, 350, 250, 5)
masked_image = Image.composite(overlay_image, kitten_image, mask_image)
masked_image.show()
Given this adorable kitten as input, the output is
EDIT: Inspired by Mark Setchell's answer, simply changing the overlay_image line to
overlay_image = kitten_image.filter(ImageFilter.GaussianBlur(radius=15))
gives us this blur variant (with smooth edges for the blur :) )
Not sure if you want to composite something over the image to conceal the contents, or blur it. This is more blurry :-)
Starting with Paddington:
You can go to "Stealth Mode" like this:
#!/usr/bin/env python3
from PIL import Image, ImageDraw, ImageFilter
import numpy as np
# Open image
im = Image.open('paddington.png')
# Make a mask the same size as the image filled with black
mask = Image.new('RGB',im.size)
# Draw a filled white circle onto the black mask
draw = ImageDraw.Draw(mask)
draw.ellipse([90,40,300,250],fill=(255,255,255))
# Blur the entire image
blurred = im.filter(ImageFilter.GaussianBlur(radius=15))
# Select either the original or the blurred image at each pixel, depending on the mask
res = np.where(np.array(mask)>0,np.array(blurred),np.array(im))
# Convert back to PIL Image and save
Image.fromarray(res).save('result.png')
Or, as suggested by #AKX, you can remove the Numpy dependency and make the code a bit smaller too yet still get same result:
#!/usr/bin/env python3
from PIL import Image, ImageDraw, ImageFilter
import numpy as np
# Open image
im = Image.open('paddington.png')
# Make a mask the same size as the image filled with black
mask = Image.new('L',im.size)
# Draw a filled white circle onto the black mask
draw = ImageDraw.Draw(mask)
draw.ellipse([90,40,300,250],fill=255)
# Blur the entire image
blurred = im.filter(ImageFilter.GaussianBlur(radius=15))
# Composite blurred image over sharp one within mask
res = Image.composite(blurred, im, mask)
# Save
res.save('result.png')
I'm trying to select the green color in an image using OpenCV (the method to do it comes from this website. The image I'm treating is :
Here is the code I tried to write.
import cv2
import matplotlib.pyplot as plt
import numpy as np
greenhsv = (60, 255, 255)
green2hsv=(70,100,170)
g_square = np.full((10, 10, 3), greenhsv, dtype=np.uint8)/255.0
plt.imshow(hsv_to_rgb(g_square))
plt.show()
g1_square = np.full((10, 10, 3), green2hsv, dtype=np.uint8)/255.0
plt.imshow(hsv_to_rgb(g1_square))
plt.show()
nucl = cv2.imread('./Pictures/image_nucleation_essai0.png')
nucl = cv2.cvtColor(nucl, cv2.COLOR_BGR2RGB)
plt.imshow(nucl)
plt.show()
hsv_nucl = cv2.cvtColor(nucl, cv2.COLOR_RGB2HSV)
mask = cv2.inRange(hsv_nucl, greenhsv,green2hsv)
result = cv2.bitwise_and(nucl, nucl, mask=mask)
plt.imshow(mask, cmap="gray")
plt.show()
plt.imshow(result)
plt.show()
The result is :
So the mask did not work.
Your color ranges are not quite right yet. Also the variables in the inRange() function are in the wrong order. It's from-to, so the darker color must be first. Change your code to cv2.inRange(hsv_nucl, green2hsv,greenhsv) You can use/tweak the values in the code below, that works.
Result:
With white background:
import numpy as np
import cv2
# load image
img = cv2.imread("Eding.png")
# convert to HSV
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
# set lower and upper color limits
lower_val = np.array([50,100,170])
upper_val = np.array([70,255,255])
# Threshold the HSV image to get only green colors
mask = cv2.inRange(hsv, lower_val, upper_val)
# apply mask to original image - this shows the green with black blackground
only_green = cv2.bitwise_and(img,img, mask= mask)
# create a black image with the dimensions of the input image
background = np.zeros(img.shape, img.dtype)
# invert to create a white image
background = cv2.bitwise_not(background)
# invert the mask that blocks everything except green -
# so now it only blocks the green area's
mask_inv = cv2.bitwise_not(mask)
# apply the inverted mask to the white image,
# so it now has black where the original image had green
masked_bg = cv2.bitwise_and(background,background, mask= mask_inv)
# add the 2 images together. It adds all the pixel values,
# so the result is white background and the the green from the first image
final = cv2.add(only_green, masked_bg)
#show image
cv2.imshow("img", final)
cv2.waitKey(0)
cv2.destroyAllWindows()
I performed a foreground extraction using grabcut on an image. Now, I want to calculate the area of the resulted foreground portion of image.
One way i thought i could do this is by thresholding image, that is foreground becomes white and background becomes black. And then by calculating the number of white pixels -
import cv2
import numpy as np
from matplotlib import pyplot as plt
img = cv2.imread('image.jpg')
mask = np.zeros(img.shape[:2],np.uint8)
bgdModel = np.zeros((1,65),np.float64)
fgdModel = np.zeros((1,65),np.float64)
rect = (1200,1050,1950,1250)
#applying grabcut
cv2.grabCut(img,mask,rect,bgdModel,fgdModel,5,cv2.GC_INIT_WITH_RECT)
mask2 = np.where((mask==2)|(mask==0),0,1).astype('uint8')
img = img*mask2[:,:,np.newaxis]
#thresholding
cvimg = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
ret,thresh = cv2.threshold(cvimg,0,255,cv2.THRESH_BINARY)
count = cv2.countNonZero(thresh)
print("White " , count)
cv2.imshow("image",thresh)
plt.imshow(img)
plt.colorbar()
plt.show()
But i doubt the accuracy as no. of pixels change when resolution of image changes. How can i find the area of foreground of images with a common unit of measurement for all images? Is there any other simple and appropriate method exists?
Thanks for helping...