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:
Related
I'm trying to transfer pixel value from one image and transferring it to other image.
so, basically I have 2 images and my goal is to transfer colors of img1 to 2 according to the regions.
link to img1 img2 and expected image
here I am aware to extract color channel out of an image , but I am not able to achieve the required result. I'll highly appreciate any help.
my approach:
import cv2
import numpy as np
import os
import matplotlib.pyplot as plt
os.chdir('/kaggle/input/maskedoutput')
stroke_list = natsorted(os.listdir())
for i,v in enumerate(stroke_list):
image = cv2.imread(v, cv2.IMREAD_UNCHANGED)
if image.shape[2] == 4:
a1 = ~image[:,:,3]
image = cv2.add(cv2.merge([a1,a1,a1,a1]), image)
image = cv2.cvtColor(image, cv2.COLOR_RGBA2RGB)
else:
image = cv2.cvtColor(image, cv2.COLOR_RGBA2RGB)
plt.imshow((image))
plt.show()
copy = image.copy()
kernel = np.ones((15,15), np.uint8)
closing = cv2.morphologyEx(copy, cv2.MORPH_CLOSE, kernel)
img_erode = cv2.erode(closing, kernel, iterations=1)# to supress black outline
height, width, channel = img_erode.shape
for x1 in range(0,width):
for y1 in range(0,height):
channels_x1y1 = img_erode[y1,x1]
os.chdir('/kaggle/input/maskstrokes')
output = natsorted(os.listdir())
for idx,v1 in enumerate(output):
if(v==v1):
print(v, v1)
img_out = cv2.imread(v1, cv2.IMREAD_UNCHANGED)
subtracted = cv2.subtract(img_out, img_erode)
else:
continue
plt.imshow(cv2.cvtColor(subtracted, cv2.COLOR_BGR2RGB))
plt.show()
here i'm meaning to first erode the original coloured image in order to supress the black outline. Then next extracting color pixels and in the image2 after reading it i'm trying to subtract it with img1 the residual would be the colored outline, but this code is not working gives mte this error:
---------------------------------------------------------------------------
error Traceback (most recent call last)
/tmp/ipykernel_33/3647166721.py in <module>
43 print(v, v1)
44 img_out = cv2.imread(v1, cv2.IMREAD_UNCHANGED)
---> 45 subtracted = cv2.subtract(img_out, img_erode)
46 # if img_out.shape[2] == 4:
47 # a1 = ~img_out[:,:,3]
error: OpenCV(4.5.4) /tmp/pip-req-build-jpmv6t9_/opencv/modules/core/src/arithm.cpp:647: error: (-209:Sizes of input arguments do not match) The operation is neither 'array op array' (where arrays have the same size and the same number of channels), nor 'array op scalar', nor 'scalar op array' in function 'arithm_op'
another approach was to directly pick color pixels from image1 and directly transfer it to second image but as you can see the image has 3 parts with different colors and so its not happening
code:
os.chdir('/kaggle/input/maskedoutput')
stroke_list = natsorted(os.listdir())
for i,v in enumerate(stroke_list):
image = cv2.imread(v, cv2.IMREAD_UNCHANGED)
if image.shape[2] == 4:
a1 = ~image[:,:,3]
image = cv2.add(cv2.merge([a1,a1,a1,a1]), image)
image = cv2.cvtColor(image, cv2.COLOR_RGBA2RGB)
else:
image = cv2.cvtColor(image, cv2.COLOR_RGBA2RGB)
plt.imshow((image))
plt.show()
copy = image.copy()
kernel = np.ones((15,15), np.uint8)
closing = cv2.morphologyEx(copy, cv2.MORPH_CLOSE, kernel)
img_erode = cv2.erode(closing, kernel, iterations=1)# to supress black outline
height, width, channel = img_erode.shape
for x1 in range(0,width):
for y1 in range(0,height):
channels_x1y1 = img_erode[y1,x1]
os.chdir('/kaggle/input/maskstrokes')
output = natsorted(os.listdir())
for idx,v1 in enumerate(output):
if(v==v1):
print(v, v1)
img_out = cv2.imread(v1, cv2.IMREAD_UNCHANGED)
height2, width2, channel2 = img_out.shape
for x1 in range(0,width2):
for y1 in range(0,height2):
channels_x1y1 = img_out[y1,x1]
else:
continue
plt.imshow(cv2.cvtColor(img_out, cv2.COLOR_BGR2RGB))
plt.show()
Blocker image
I prepared a quick fix solution based on the expected output.
I used the following image as input:
Code:
img = cv2.imread('colored_objects.png')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# binary mask
mask = cv2.threshold(gray,10,255,cv2.THRESH_BINARY)[1]
# inverted binary mask
th = cv2.threshold(gray,10,255,cv2.THRESH_BINARY_INV)[1]
# finding external contours based on inverted binary mask
contours, hierarchy = cv2.findContours(th, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
# create copy of original image to draw contours
img2 = img.copy()
In the following, we iterate through each contour.
For each contour:
get the centroid of the contour (centroid)
get the color at the centroid from original image (color)
draw the contour with that color on the copy of original image (img2)
code:
for c in contours:
M = cv2.moments(c)
cx = int(M['m10']/M['m00'])
cy = int(M['m01']/M['m00'])
centroid = (cx, cy)
color = tuple(img[cy, cx])
color = ( int (color [ 0 ]), int (color [ 1 ]), int (color [ 2 ]))
print(color)
img2 = cv2.drawContours(img2, [c], 0, tuple(color), -1)
Now we subtract the original from the newly drawn image r. Based on mask, wherever pixels are white we assign white in r
r = img2 - img
r[mask == 255] = (255, 255, 255)
Update:
Expected result for the latest image. The green shade is present on the border as expected. This was obtained using the same code without any changes:
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:
I need to write script in Python, like this with options:
Enable PNG transparency
Treat similar colors as transparent. 5%
How can I do this?
Potential solutions
ffmpeg, imagemagick
Here is one way to do that in Python/OpenCV
Read the input
Define the desired color (blue) to be made transparent
Define the tolerance amount (5%)
Create upper and lower bounds on the color according to the tolerance
Threshold on color and invert
Put the threshold result into the alpha channel of the input
Save the results
Input:
import cv2
import numpy as np
# load image
img = cv2.imread('barn.jpg')
# specify blue color
color = (230,160,120)
b = color[0]
g = color[1]
r = color[2]
# specify tolerance as percent
tol = 5
tol = 5/100
# make lower and upper bounds as color minus/plus 5%
bl = int((1-tol) * b)
gl = int((1-tol) * g)
rl = int((1-tol) * r)
lower = (bl,gl,rl)
bu = int((1+tol) * b)
gu = int((1+tol) * g)
ru = int((1+tol) * r)
upper = (bu,gu,ru)
# threshold on color and invert
mask = cv2.inRange(img, lower, upper)
mask = 255 - mask
# put mask into alpha channel
result = img.copy()
result = cv2.cvtColor(result, cv2.COLOR_BGR2BGRA)
result[:, :, 3] = mask
# save resulting masked image
cv2.imwrite('barn_transp_blue.png', result)
# display result, though it won't show transparency
cv2.imshow("MASK", mask)
cv2.imshow("RESULT", result)
cv2.waitKey(0)
cv2.destroyAllWindows()
Threshold image:
Transparent result:
img = Image.open(images_dir +'/'+ filename)
img.save(create_path +'/'+ filename.split('.')[0]+'.png', 'png')
enter image description here
How to make this picture no longer sawtooth around? Without changing the color array.
Here is one way to do that by blurring a mask in Python/OpenCV.
Read the input
Convert to gray
Threshold to create a mask
Blur the mask and stretch so that 127.5 goes to 0 and 255 stays at 255
Convert the mask to float in range 0 to 1
Multiply the input by the mask and convert back to 8-bit integer and clip to range 0 to 255
Save the results
Input:
import cv2
import numpy as np
import skimage.exposure
# load image
img = cv2.imread('man.jpg')
# convert to gray
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
# threshold
thresh = cv2.threshold(gray, 16, 255, cv2.THRESH_BINARY)[1]
# blur threshold image
blur = cv2.GaussianBlur(thresh, (0,0), sigmaX=5, sigmaY=5, borderType = cv2.BORDER_DEFAULT)
# stretch so that 255 -> 255 and 127.5 -> 0
mask = skimage.exposure.rescale_intensity(blur, in_range=(127.5,255), out_range=(0,255)).astype(np.float32) / 255
mask = cv2.merge([mask,mask,mask])
# replace alpha channel in input with new alpha channel
result = (mask * img).clip(0,255).astype(np.uint8)
# save output
cv2.imwrite('man_thresh.png', thresh)
cv2.imwrite('man_mask.png', mask)
cv2.imwrite('man_antialiased.png', result)
# Display various images to see the steps
cv2.imshow('gray',gray)
cv2.imshow('thresh', thresh)
cv2.imshow('mask', mask)
cv2.imshow('result', result)
cv2.waitKey(0)
cv2.destroyAllWindows()
Threshold image:
Mask image:
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)