While studying OpenCV, I realized that whenever I blend two images the colors of scr2 have changed in some way(depends on the colors of scr1).
I know this is not an informative and clear way to explain my issue, however; I don't know how to describe this issue since I have no expertise with colors so I would like to show you what I meant with images and code.
The input image: Input image
import cv2
import numpy as np
img=cv2.imread("inputImage.png")
scr2=255*np.ones((img.shape[0],img.shape[1],3),dtype=np.uint8)
#adding lines
cv2.line(scr2,(100,0),(100,img.shape[1]),(0,255,0),3)
cv2.line(scr2,(300,0),(300,img.shape[1]),(255,0,0),3)
cv2.line(scr2,(500,0),(500,img.shape[1]),(0,0,255),3)
#blending
blend=cv2.addWeighted(img,0.7,scr2,0.3,0)
crop=blend[60:100,460:530]
cv2.imwrite("crop.png",crop)
cv2.imwrite("line.png",blend)
cropped in red line As you can see, even though I added a single color for each line, the colors of lines have changed depends on the background color. The red line does not seem to be red in the cropped image.
Can you elaborate on why this happening and how can I avoid this problem? I mean, I don't want the color change on lines.
Thank you for your attention.
In case you want the blend to resembled perceptual color blending (closer to the expected blend color by humans), you may apply the blending in LAB color space.
Advantage:
Unlike the RGB and CMYK color models, CIELAB is designed to approximate human vision.
I have demonstrated the principal in my following answer (at the bottom).
img
and scr2
from BGR to LAB color space.addWeighted
in LAB color space.Since the blending of white background of scr2
bothers me, I also copy the original pixels from img
to blend
where pixels in scr2
are white.
Here is a complete code sample:
import cv2
import numpy as np
img = cv2.imread("inputImage.png")
scr2 = np.full_like(img, 255)
img_lab = cv2.cvtColor(img, cv2.COLOR_BGR2LAB) # Convert color space from BGR to LAB color space
# adding lines
cv2.line(scr2, (100, 0), (100, img.shape[1]), (0, 255, 0), 3)
cv2.line(scr2, (300, 0), (300, img.shape[1]), (255, 0, 0), 3)
cv2.line(scr2, (500, 0), (500, img.shape[1]), (0, 0, 255), 3)
scr2_lab = cv2.cvtColor(scr2, cv2.COLOR_BGR2LAB) # Convert color space from BGR to LAB color space
# blending
#blend = cv2.addWeighted(img, 0.7, scr2, 0.3, 0)
blend_lab = cv2.addWeighted(img_lab, 0.7, scr2_lab, 0.3, 0) # Blend images in LAB color space
blend = cv2.cvtColor(blend_lab, cv2.COLOR_LAB2BGR) # Convert color space from LAB to BGR color space
# Replace blended "background" white pixels of scr2 with original pixel values from img:
mask = np.all(scr2 == 255, 2).astype(np.uint8) # Mask value is 1 where 3 color channels are 255
cv2.copyTo(img, mask, blend) # Copy pixels from img to blend where mask != 0.
crop = blend[60:100, 460:530]
cv2.imwrite("crop2.png", crop)
cv2.imwrite("line2.png", blend)
# Show the results
cv2.imshow('mask', mask*255)
cv2.imshow('img', img)
cv2.imshow('blend', blend)
cv2.imshow('crop', crop)
cv2.waitKey()
cv2.destroyAllWindows()
Result:
left: crop
blended in LAB space.
right: crop
blended in BGR space.
Note:
Copy the luminance from src2
to blend
:
According to your comment, it looks like you want to keep the luminance of the colored lines from src2
, and not blend the luminance with the background.
In LAB color space, the L color channel is the luminance.
You may copy the luminance from src2
to blend
where src2
is not white.
Here is the relevant part of the code:
blend_lab = cv2.addWeighted(img_lab, 0.7, scr2_lab, 0.3, 0) # Blend images in LAB color space
# Get the luminance channel (get NumPy slice)
scr2_luminance = scr2_lab[:, :, 0]
blend_luminance = blend_lab[:, :, 0]
blend_luminance[np.any(scr2 != 255, 2)] = scr2_luminance[np.any(scr2 != 255, 2)] # Copy the luminace from scr2_lab to blend_lab where scr2 is not white.
blend = cv2.cvtColor(blend_lab, cv2.COLOR_LAB2BGR) # Convert color space from LAB to BGR color space
Note:
We may copy the saturation channel from scr2
to blend
in HSV color space:
scr2_hsv = cv2.cvtColor(scr2, cv2.COLOR_BGR2HSV)
blend_hsv = cv2.cvtColor(blend, cv2.COLOR_BGR2HSV)
blend_hsv[:,:,1][np.any(scr2 != 255, 2)] = scr2_hsv[:,:,1][np.any(scr2 != 255, 2)] # Copy the saturation channel.
blend = cv2.cvtColor(blend_hsv, cv2.COLOR_HSV2BGR)
I am not sure that it makes sense...