Reputation: 651
I have two pictures called pic1.jpg and pic2.jpg, and these two pictures are of the same size (same width, same height).
I want to take those parts whose color is yellow (rgb=255,255,0) from pic1, and then draw them to pic2 at the same position.
How can I do this via opencv-python? I googled and tried below code, but it doesn't work.
image1 = cv2.imread('pic1.jpg')
image2 = cv2.imread('pic2.jpg')
hsv = cv2.cvtColor(image1, cv2.COLOR_BGR2HSV)
# only want the yellow parts
lower_color = np.array([0, 255, 255])
upper_color = np.array([0, 255, 255])
#
mask = cv2.inRange(hsv, lower_color, upper_color)
# add them to image2
result = cv2.bitwise_and(image2, image2, mask=mask)
cv2.imwrite('final.jpg', result)
Upvotes: 1
Views: 786
Reputation: 32144
We can't use cv2.bitwise_and
for replacing masked pixels in image2
with pixels from image1
.
In C++ we may use mat::copyTo with mask for doing that, but in Python, we can't use copyTo
, because it cannot be used with NumPy arrays.
We may solve it using something like result = cv2.bitwise_or(cv2.bitwise_and(image1, mask), cv2.bitwise_and(image2, cv2.bitwise_not(mask)))
.
But using NumPy logical indexing seems more elegant.
Note:
As commented, [0, 255, 255]
is red in HSV.
We don't have to convert to HSV for finding yellow pixels.
If we do, the yellow value is [30, 255, 255]
in HSV.
For applying logical indexing or bitwise operations we have to make mask the same dimensions as the images.
Using OpenCV: mask = cv2.cvtColor(mask, cv2.COLOR_GRAY2BGR)
replicates the mask to 3 axes.
Code sample:
import cv2
import numpy as np
image1 = cv2.imread('pic1.jpg')
image2 = cv2.imread('pic2.jpg')
hsv = cv2.cvtColor(image1, cv2.COLOR_BGR2HSV)
cv2.imwrite('hsv.png', hsv)
# Only want the yellow parts. Yellow in HSV equls [30, 255, 255]
lower_color = np.array([28, 250, 250])
upper_color = np.array([32, 255, 255])
mask = cv2.inRange(hsv, lower_color, upper_color)
mask = cv2.cvtColor(mask, cv2.COLOR_GRAY2BGR) # Convert maks to 3D array - as np.concatenate((mask,mask,mask))
#result = cv2.bitwise_or(cv2.bitwise_and(image1, mask), cv2.bitwise_and(image2, cv2.bitwise_not(mask))) # Pure OpenCV solution.
result = image2
result[mask==255] = image1[mask==255] # Use logical indexing for replacing the masked pixels in image2 with pixels from image1.
cv2.imwrite('final.jpg', result)
# Write mask for testing
cv2.imwrite('mask.jpg', mask)
The following images were used for testing:
Code sample without converting to HSV color space:
Conversion to HSV is useful for finding larger range of yellow (yellowish) colored pixels.
Example: Searching pixels with Hue=30, Saturation in range [100, 255] and Value in range [30, 255] returns large range of yellow pixels (dark yellow, bright yellow...).
When looking for pure bright yellow, we may apply cv2.inRange
to BGR color format, and search for pixels with Blue=0, Green=255, Red=255.
The example uses a bit wider range [0, 250, 250]
to [5, 255, 255]
(mainly because the JPEG compression modifies the original values).
Code sample, without converting to HSV:
import cv2
import numpy as np
image1 = cv2.imread('pic1.jpg')
image2 = cv2.imread('pic2.jpg')
# Only want the yellow parts. Yellow in BGR equls [0, 255, 255]
lower_color = np.array([0, 250, 250])
upper_color = np.array([5, 255, 255])
mask = cv2.inRange(image1, lower_color, upper_color) # Apply inRange to image1 in BGR color format
mask = cv2.cvtColor(mask, cv2.COLOR_GRAY2BGR) # Convert maks to 3D array - as np.concatenate((mask,mask,mask))
result = image2
result[mask==255] = image1[mask==255] # Use logical indexing for replacing the masked pixels in image2 with pixels from image1.
cv2.imwrite('final.jpg', result)
Upvotes: 3