fozbstuios
fozbstuios

Reputation: 373

Why doesn't python openCV change colors the way I expect it to?

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:

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.

original file

Upvotes: 1

Views: 989

Answers (2)

Ujjwal Saxena
Ujjwal Saxena

Reputation: 409

Iterated over all unique RGB values the picture has and whoa!! those are too many colors actually.

enter image description here

@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]]

enter image description here

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

ZdaR
ZdaR

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)

enter image description here

To further improve the results you can use HSV color range instead of BGR color range.

Upvotes: 2

Related Questions