Reputation: 373
I am just starting out with opencv in python3(or any language for that matter). I am on windows 10. Here is my pip freeze:
numpy==1.16.0
opencv-python==4.0.0.21
Wand==0.5.0
I am trying to change every color pixel except for (BGR notation)[255,142,0] to black. Here is my test code
import cv2
import numpy as np
img=cv2.imread('referenceFont.png')
cv2.imshow('original',img)
img[np.where((img!=[255,142,0]).all(axis=2))]=[0,0,0]
cv2.imshow('remove other colors',img)
img[np.where((img==[255,255,255]).all(axis=2))]=[0,0,0]
cv2.imshow('explicit remove white',img)
cv2.waitKey(0)
cv2.destroyAllWindows()
Note that the image did originally have transparency, if that matters. I thought not loading with transparency would help simplify things. Here is the output:
As you can see, just trying to change the non matching colors results in an almost unreadable image, and even when I explicitly remove white there is still a "halo effect". I'd basically like the original image, but with a black background. Thanks for any help.
update 1
Per request, here is the original file.
Upvotes: 1
Views: 989
Reputation: 409
Iterated over all unique RGB values the picture has and whoa!! those are too many colors actually.
@ZdaR is correct you should remove a range of colors and not just a specific color tuple. You can use this snippet to visualize the unique color pixels.
import cv2
import numpy as np
import matplotlib.pyplot as plt
img=cv2.imread('Desktop/image.png')
arr=[]
for j in range(img.shape[1]):
for i in range(img.shape[0]):
arr.append([img[i,j,0],img[i,j,1],img[i,j,2]])
cols,img,j,k=np.unique(np.array(arr), axis=0),np.zeros((9192,9192,3), np.uint8),0,0
for i in cols:
cv2.circle(img,(100+j*200,100+k*200), 85,(int(i[0]),int(i[1]),int(i[2])), -1)
j+=1
if j%45==0:
k+=1
j=0
cv2.imwrite('Desktop/colors.png',img)
and for extracting the blue components, similar answer as above:
import cv2
import numpy as np
import matplotlib.pyplot as plt
img=cv2.imread('Desktop/image.png')
mask = cv2.inRange(img, np.array([200,100,0]), np.array([255, 170, 255])) #single channel mask
mask= np.dstack((mask,mask,mask)) #this just makes the mask a 3 channel image
cv2.imwrite('Desktop/colors.png',cv2.min(img, mask))
What cv2.min() does is "element wise comparison" which is comparing 1st channel value to 1st channel value, 2nd to 2nd and so on.
white_pixel=np.array([255,255,255])
mask=np.array([0,0,0])
print(cv2.min(white_pixel,mask))
#prints [[0][0][0]]
blue_pixel_of_some_shade=np.array([255,223,122])#not sure which shade is this
mask=np.array([255,255,255])
print(cv2.min(blue_pixel_of_some_shade,mask))
#prints [[255][223][122]]
P.S You can also just multiply the mask with the image if the mask has 0s and 1s instead of 0s and 255s.
Upvotes: 2
Reputation: 22954
It is advised to perform the color segmentation operations in HSV color range instead of BGR color range. Also it is worth noting that your input image has anti-aliasing colors, which means that it contains some pixels with diffused white and blue values. To tackle these kind of input images, we can use cv2.inRange()
method instead of a single ==
operation for a given color.
import cv2
import numpy as np
img = cv2.imread("/path/to/img.png")
mask = cv2.inRange(img, np.array([200,100,0]), np.array([255, 170, 255]))
mask = cv2.cvtColor(mask, cv2.COLOR_GRAY2BGR)
final = cv2.min(img, mask)
To further improve the results you can use HSV color range instead of BGR color range.
Upvotes: 2