So I currently have 2 images below. These were obtained using a neural style transfer network on the foreground and background parts of the image.
The code used was the following:
add = cv2.add(image1, image2)
cv2.imshow('Addition', add)
cv2.waitKey(0)
cv2.destroyAllWindows()
I have merged them to create the following resultant image:
However, I want to use the salience mask on the obtained picture. The salience mask is below. I have tried inputting the mask argument to the add function but had no luck. I've tried looking at this article but wasn't able to work out what to do Add 2 images together based on a mask
EDIT:
Found the issue was that I was not converting the data types. The updated code is:
saliencyMap = cv2.resize(saliencyMap, (384,384))
newmap = saliencyMap.astype(np.uint8)
add = cv2.add(image1, image2, mask=newmap)
cv2.imshow('addition', add)
cv2.waitKey(0)
However, the resultant image is and I'm not sure why:
I think what you might want is to simply resize the two images to the same size and then add them. Then use the mask to do hard light compositing in Python/OpenCV.
img1:
img2:
mask (saliency):
import cv2
import numpy as np
# read image 1
img1 = cv2.imread('img1.png')
hh, ww = img1.shape[:2]
# read image 2 and resize to same size as img1
img2 = cv2.imread('img2.png')
img2 = cv2.resize(img2, (ww,hh))
# read saliency mask as grayscale and resize to same size as img1
mask = cv2.imread('mask.png')
mask = cv2.cvtColor(mask, cv2.COLOR_BGR2GRAY)
mask = cv2.resize(mask, (ww,hh))
# add img1 and img2
img12 = cv2.add(img1, img2)
# get mean of mask and shift mean to mid-gray
# desirable for hard light compositing
# add bias as needed
mean = np.mean(mask)
bias = -32
shift = 128 - mean + bias
mask = cv2.add(mask, shift)
mask = cv2.merge([mask,mask,mask])
# threshold mask at mid gray and convert to 3 channels
# (needed to use as src < 0.5 "if" condition in hard light)
thresh = cv2.threshold(mask, 128, 255, cv2.THRESH_BINARY)[1]
# do hard light composite of img12 and mask
# see CSS specs at https://www.w3.org/TR/compositing-1/#blendinghardlight
img12f = img12.astype(np.uint8)/255
maskf = mask.astype(np.uint8)/255
threshf = thresh.astype(np.uint8)/255
threshf_inv = 1 - threshf
low = 2.0 * img12f * maskf
high = 1 - 2.0 * (1-img12f) * (1-maskf)
result = ( 255 * (low * threshf_inv + high * threshf) ).clip(0, 255).astype(np.uint8)
# save results
cv2.imwrite('img12_hardlight.png', result)
# show results
cv2.imshow('img12', img12)
cv2.imshow('mask', mask)
cv2.imshow('thresh', thresh)
cv2.imshow('result', result)
cv2.waitKey(0)
cv2.destroyAllWindows()
Result:
Related
Sample Image:
Here's my code
import cv2
import numpy as np
image = cv2.imread("1.bmp")
img_grey = cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)
ret, thresh1 = cv2.threshold(img_grey,120,255,cv2.THRESH_BINARY)
ret, thresh = cv2.threshold(img_grey,120,255,cv2.THRESH_BINARY_INV)
cv2.imshow("image 1", thresh1)
cv2.imshow("image 2", thresh)
cv2.waitKey(0)
Any idea to segment the grey region ? I thought some sort of subtracting the white and black region using raw image but not working.
Here are two ways to do that in Python/OpenCV/Numpy. The first method uses cv2.inRange() color thresholding. The second method use Numpy. Both threshold so that the gray region becomes white and the rest black.
Input:
import cv2
import numpy as np
# read input
img = cv2.imread("white_gray_black.png")
# Method 1 Use cv2.inRange
low = (127,127,127)
high = (127,127,127)
mask1 = cv2.inRange(img, low, high)
cv2.imwrite("white_gray_black_mask1.png",mask1)
# Method 2 Use Numpy
mask2 = img.copy()
mask2[img==127] = 255
mask2[img!=127 ] = 0
cv2.imwrite("white_gray_black_mask2.png",mask2)
cv2.imshow('mask1', mask1)
cv2.imshow('mask2', mask2)
cv2.waitKey(0)
cv2.destroyAllWindows()
Result 1:
Result 2:
If you need to preserve the gray, then the second method is easier.
# Method 3 Use Numpy
mask3 = img.copy()
mask3[img!=127] = 0
cv2.imwrite("white_gray_black_mask3.png",mask3)
cv2.imshow('mask3', mask3)
cv2.waitKey(0)
cv2.destroyAllWindows()
Option 1:
You need to place different threshold values but the same binary operation. Then try subtracting the result:
ret, thresh1 = cv2.threshold(img_grey,20,255,cv2.THRESH_BINARY)
ret, thresh2 = cv2.threshold(img_grey,235,255,cv2.THRESH_BINARY)
result = thresh1 - thresh2
cv2.imshow("Result", result)
cv2.waitKey(0)
See the images of thresh1 and thresh2 to understand what's going on.
And if you are worried about how to subtract use cv2.subtract(), it handles pixel range:
result2 = cv2.subtract(thresh1, thresh2)
cv2.imshow("Result2", result2)
cv2.waitKey(0)
The output does not provide the desired result.
Option 2:
You can also use multi-Otsu threshold module available from skimage. This module provides threshold values based on the number of regions you want to segment the image.
If you want to segment the image into 3 regions (A, B, C), the function gives you 2 threshold values (t1, t2).
0 - t1 --> region A
t1 - t2 --> region B
t2 - 255 --> region C
The same can be extended for any number of regions, but the function takes more time to calculate threshold values for regions greater than 5.
Number of Threshold values = Number of regions - 1
Code:
# import the module
from skimage.filters import threshold_multiotsu
im = cv2.imread('cells.png', 0)
# create black screen of same image shape
b = np.zeros(im.shape, np.uint8)
# find threshold values for 3 regions
thresholds = threshold_multiotsu(im,3)
print(thresholds)
>>> [ 65 189] # 2 threshold values
# finding average value
mean = int(np.mean(thresholds))
b[im == mean] = 255
Option 2 would be a more statistical way of determining threshold value for any image
I currently have the following image and the salience map below which reflects the attention areas of the first image:
Both of them are the same size. What I am trying to do is amplify the region of areas that are very white in the salient region. For example, the eyes, collar and hair would be a bit more highlighted. I have the following code which I have tried to split the image into h, s, v and then multiply through but the output is black and white. Any help is greatly appreciated:
hsv_image = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
h, s, v = cv2.split(hsv_image)
dimensions = (384, 384)
saliencyMap = cv2.resize(saliencyMap, dimensions)
s1 = s * saliencyMap.astype(s.dtype)
hsv_image = cv2.merge([h, s1, v])
out = cv2.cvtColor(hsv_image, cv2.COLOR_HSV2BGR)
cv2.imshow('example', out)
cv2.waitKey()
Here is how to do that in Python/OpenCV. Add the two images (from your other post). Modify the mask to have values near a mean of mid-gray. Separate the image into H,S,V channels. Apply the mask to the Saturation channel doing hard light composition. Combine the new saturation with the old hue and value channels and convert back to BGR.
Input 1:
Input 2:
Mask:
import cv2
import numpy as np
# read image 1
img1 = cv2.imread('img1.png')
hh, ww = img1.shape[:2]
# read image 2 and resize to same size as img1
img2 = cv2.imread('img2.png')
img2 = cv2.resize(img2, (ww,hh))
# read saliency mask as grayscale and resize to same size as img1
mask = cv2.imread('mask.png')
mask = cv2.cvtColor(mask, cv2.COLOR_BGR2GRAY)
mask = cv2.resize(mask, (ww,hh))
# add img1 and img2
img12 = cv2.add(img1, img2)
# get mean of mask and shift mean to mid-gray
# desirable for hard light compositing
# add bias as needed
mean = np.mean(mask)
bias = -32
shift = 128 - mean + bias
mask = cv2.add(mask, shift)
# threshold mask at mid gray and convert to 3 channels
# (needed to use as src < 0.5 "if" condition in hard light)
thresh = cv2.threshold(mask, 128, 255, cv2.THRESH_BINARY)[1]
# convert img12 to hsv
hsv = cv2.cvtColor(img12, cv2.COLOR_BGR2HSV)
# separate channels
hue,sat,val = cv2.split(hsv)
# do hard light composite of saturation and mask
# see CSS specs at https://www.w3.org/TR/compositing-1/#blendinghardlight
satf = sat.astype(np.uint8)/255
maskf = mask.astype(np.uint8)/255
threshf = thresh.astype(np.uint8)/255
threshf_inv = 1 - threshf
low = 2.0 * satf * maskf
high = 1 - 2.0 * (1-satf) * (1-maskf)
new_sat = ( 255 * (low * threshf_inv + high * threshf) ).clip(0, 255).astype(np.uint8)
# combine new_sat with old hue and val
result = cv2.merge([hue,new_sat,val])
# save results
cv2.imwrite('img12_sat_hardlight.png', result)
# show results
cv2.imshow('img12', img12)
cv2.imshow('mask', mask)
cv2.imshow('thresh', thresh)
cv2.imshow('result', result)
cv2.waitKey(0)
cv2.destroyAllWindows()
Result:
I have detected two lines in an image using cv2. now I want to get the RGB values of both lines in separate variables like left_line_veriable = ['rgb values'], right_line_rgb_values = ['rgb values']
Here is my code:
import cv2
import numpy as np
image = cv2.imread('tape.png')
image = cv2.cvtCOLOR(image, cv2.COLOR_BGR2GRAY)
# Apply adaptive threshold
image_thr = cv2.adaptiveThreshold(image, 255, cv2.THRESH_BINARY_INV, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, 81, 2)
# Apply morphological opening with vertical line kernel
kernel = np.ones((image.shape[0], 1), dtype=np.uint8) * 255
image_mop = cv2.morphologyEx(image_thr, cv2.MORPH_OPEN, kernel)
color_detected_img = cv2.bitwise_and(image, image, mask=image_mop)
cv2.imshow('image', color_detected_img)
cv2.waitKey(0)
cv2.destroyAllWindows()
This is the image from which I want to get both line's RGB values in two variables as described above:
Maybe is not the most optimal way, but it is not hard to do. As I said in my comments, you can label the image to kind of segment the lines, then get the mean of the rgb values in it and the average position to get to know which one is left and right. Here is a small script to demonstrate what I am saying. The last part is just to show the results.
import cv2
import numpy as np
# load img and get the greyscale
img = cv2.imread("x.png")
grey = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# label the image
ret, thres = cv2.threshold(grey, 1, 255, cv2.THRESH_BINARY)
labelAmount, labels = cv2.connectedComponents(thres)
# get the mean of the color and position
values = []
# first label (0) is background
for i in range(1, labelAmount):
mask = np.zeros(labels.shape, dtype=np.uint8)
mask[labels == i] = 255
mean = cv2.mean(img, mask)[:-1]
meanPos = np.mean(cv2.findNonZero(mask), axis=0)[0]
values.append((mean, meanPos))
# sort them by x value (left to right)
values = sorted(values, key = lambda v : v[1][0])
left_line_color = values[0][0]
right_line_color = values[1][0]
# just to show the results
left_only = np.zeros(img.shape, dtype=np.uint8)
right_only = np.zeros(img.shape, dtype=np.uint8)
left_only = cv2.line (left_only, (int(values[0][1][0]), 0), (int(values[0][1][0]), img.shape[0]), left_line_color,5 )
right_only = cv2.line (right_only, (int(values[1][1][0]), 0), (int(values[1][1][0]), img.shape[0]), right_line_color,5 )
cv2.imshow("left_line", left_only)
cv2.imshow("right_line", right_only)
cv2.imshow("original", img)
cv2.waitKey(0)
I'm trying to crop an object from an image, and paste it on another image. Examining the method in this answer, I've successfully managed to do that. For example:
The code (show_mask_applied.py):
import sys
from pathlib import Path
from helpers_cv2 import *
import cv2
import numpy
img_path = Path(sys.argv[1])
img = cmyk_to_bgr(str(img_path))
threshed = threshold(img, 240, type=cv2.THRESH_BINARY_INV)
contours = find_contours(threshed)
mask = mask_from_contours(img, contours)
mask = dilate_mask(mask, 50)
crop = cv2.bitwise_or(img, img, mask=mask)
bg = cv2.imread("bg.jpg")
bg_mask = cv2.bitwise_not(mask)
bg_crop = cv2.bitwise_or(bg, bg, mask=bg_mask)
final = cv2.bitwise_or(crop, bg_crop)
cv2.imshow("debug", final)
cv2.waitKey(0)
cv2.destroyAllWindows()
helpers_cv2.py:
from pathlib import Path
import cv2
import numpy
from PIL import Image
from PIL import ImageCms
from PIL import ImageFile
ImageFile.LOAD_TRUNCATED_IMAGES = True
def cmyk_to_bgr(cmyk_img):
img = Image.open(cmyk_img)
if img.mode == "CMYK":
img = ImageCms.profileToProfile(img, "Color Profiles\\USWebCoatedSWOP.icc", "Color Profiles\\sRGB_Color_Space_Profile.icm", outputMode="RGB")
return cv2.cvtColor(numpy.array(img), cv2.COLOR_RGB2BGR)
def threshold(img, thresh=128, maxval=255, type=cv2.THRESH_BINARY):
if len(img.shape) == 3:
img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
threshed = cv2.threshold(img, thresh, maxval, type)[1]
return threshed
def find_contours(img):
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (11,11))
morphed = cv2.morphologyEx(img, cv2.MORPH_CLOSE, kernel)
contours = cv2.findContours(morphed, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
return contours[-2]
def mask_from_contours(ref_img, contours):
mask = numpy.zeros(ref_img.shape, numpy.uint8)
mask = cv2.drawContours(mask, contours, -1, (255,255,255), -1)
return cv2.cvtColor(mask, cv2.COLOR_BGR2GRAY)
def dilate_mask(mask, kernel_size=11):
kernel = numpy.ones((kernel_size,kernel_size), numpy.uint8)
dilated = cv2.dilate(mask, kernel, iterations=1)
return dilated
Now, instead of sharp edges, I want to crop with feathered/smooth edges. For example (the right one; created in Photoshop):
How can I do that?
All images and codes can be found that at this repository.
You are using a mask to select parts of the overlay image. The mask currently looks like this:
Let's first add a Gaussian blur to this mask.
mask_blurred = cv2.GaussianBlur(mask,(99,99),0)
We get to this:
Now, the remaining task it to blend the images using the alpha value in the mask, rather than using it as a logical operator like you do currently.
mask_blurred_3chan = cv2.cvtColor(mask_blurred, cv2.COLOR_GRAY2BGR).astype('float') / 255.
img = img.astype('float') / 255.
bg = bg.astype('float') / 255.
out = bg * (1 - mask_blurred_3chan) + img * mask_blurred_3chan
The above snippet is quite simple. First, transform the mask into a 3 channel image (since we want to mask all the channels). Then transform the images to float, since the masking is done in floating point. The last line does the actual work: for each pixel, blends the bg and img images according to the value in the mask. The result looks like this:
The amount of feathering is controlled by the size of the kernel in the Gaussian blur. Note that it has to be an odd number.
After this, out (the final image) is still in floating point. It can be converted back to int using:
out = (out * 255).astype('uint8')
While Paul92's answer is more than enough, I wanted to post my code anyway for any future visitor.
I'm doing this cropping to get rid of white background in some product photos. So, the main goal is to get rid of the whites while keeping the product intact. Most of the product photos have shadows on the ground. They are either the ground itself (faded), or the product's shadow, or both.
While the object detection works fine, these shadows also count as part of the object. Differentiating the shadows from the objects is not really necessary, but it results in some images that are not so desired. For example, examine the left and bottom sides of the image (shadow). The cut/crop is obviously visible, and doesn't look all that nice.
To get around this problem, I wanted to do non-rectangular crops. Using masks seems to do the job just fine. The next problem was to do the cropping with feathered/blurred edges so that I can get rid of these visible shadow cuts. With the help of Paul92, I've managed to do that. Example output (notice the missing shadow cuts, the edges are softer):
Operations on the image(s):
The code (show_mask_feathered.py, helpers_cv2.py)
import sys
from pathlib import Path
import cv2
import numpy
from helpers_cv2 import *
img_path = Path(sys.argv[1])
img = cmyk_to_bgr(str(img_path))
threshed = threshold(img, 240, type=cv2.THRESH_BINARY_INV)
contours = find_contours(threshed)
dilation_length = 51
blur_length = 51
mask = mask_from_contours(img, contours)
mask_dilated = dilate_mask(mask, dilation_length)
mask_smooth = smooth_mask(mask_dilated, odd(dilation_length * 1.5))
mask_blurred = cv2.GaussianBlur(mask_smooth, (blur_length, blur_length), 0)
mask_blurred = cv2.cvtColor(mask_blurred, cv2.COLOR_GRAY2BGR)
mask_threshed = threshold(mask_blurred, 1)
mask_contours = find_contours(mask_threshed)
mask_contour = max_contour(mask_contours)
x, y, w, h = cv2.boundingRect(mask_contour)
img_cropped = img[y:y+h, x:x+w]
mask_cropped = mask_blurred[y:y+h, x:x+w]
background = numpy.full(img_cropped.shape, (200,240,200), dtype=numpy.uint8)
output = alpha_blend(background, img_cropped, mask_cropped)
I'm trying to cartoonify a face using opencv.Here's the original image
Currently I'm doing
Downscaling the image, applying bifilter and upscaling back to original
Then converting RGB of original image to grayscale and followed
medianblur to reduce nice
Apply Adaptive Threshold to create edgemask
Combining the image obtained from step1 with the edge mask with
bitmap
Here's the output
Then applied non-photorealistic rendering using OpenCV. Here's the final output
I want to generate face with uniform color(remove light reflection as well)without affecting the eyes, mouth. How can I achieve that either by tweaking my current code or another possible approach in opencv(python)
Based on: https://www.pyimagesearch.com/2014/07/07/color-quantization-opencv-using-k-means-clustering/
Here is a code that does what you are looking for:
import cv2
import numpy as np
from sklearn.cluster import MiniBatchKMeans
n = 32
# read image and convert to gray
img = cv2.imread('./obama.jpg',cv2.IMREAD_COLOR)
img = cv2.resize(img, (0,0), fx=.2, fy=.2)
img = cv2.cvtColor(img, cv2.COLOR_BGR2LAB)
(h, w) = img.shape[:2]
img =np.reshape(img, (img.shape[0]* img.shape[1], 3))
clt = MiniBatchKMeans(n_clusters=n)
labels = clt.fit_predict(img)
quant = clt.cluster_centers_.astype("uint8")[labels]
quant = np.reshape(quant, (h,w,3))
img = np.reshape(img, (h,w,3))
quant = cv2.cvtColor(quant, cv2.COLOR_LAB2BGR)
img = cv2.cvtColor(img, cv2.COLOR_LAB2BGR)
double = np.hstack([img, quant])
while True:
cv2.imshow('img', double)
k = cv2.waitKey(30) & 0xff
if k == 27:
break
You can use this tutorial to apply the color quantization only to boxes containing faces.
https://realpython.com/face-recognition-with-python/