Reputation:
How can I convert the light gray background color (with variation in its shades) to a single color, say light gray? I want the dark gray color embedded on top of the background color to be one single color as well. Until now, what I have done is to assign these two regions with two closest color names ('lightgray' and 'dimgray') and minimize the sum of squared differences for each pixel's r,g,b and get the closest color from webcolors module.
rd = (r_c - requested_color[0]) ** 2
gd = (g_c - requested_color[1]) ** 2
bd = (b_c - requested_color[2]) ** 2
Original image:
Image produced from my python code:
You can see that that along the boundaries I am not able to get the light gray color as you could see in the original image.
Upvotes: 1
Views: 1667
Reputation: 6259
What you want to do is called thresholding.
A general introduction of how to do this with OpenCV and Python can be found here: http://docs.opencv.org/3.1.0/d7/d4d/tutorial_py_thresholding.html#gsc.tab=0
I have adapted some parameters of the code there:
import cv2
import numpy as np
from matplotlib import pyplot as plt
img = cv2.imread('image.png',0)
img = cv2.medianBlur(img,7)
ret,th1 = cv2.threshold(img,160,255,cv2.THRESH_BINARY)
th2 = cv2.adaptiveThreshold(img,255,cv2.ADAPTIVE_THRESH_MEAN_C,cv2.THRESH_BINARY,31,5)
th3 = cv2.adaptiveThreshold(img,255,cv2.ADAPTIVE_THRESH_GAUSSIAN_C,cv2.THRESH_BINARY,31,5)
titles = ['Original Image', 'Global Thresholding (v = 127)',
'Adaptive Mean Thresholding', 'Adaptive Gaussian Thresholding']
images = [img, th1, th2, th3]
for i in xrange(4):
plt.subplot(2,2,i+1),plt.imshow(images[i],'gray')
plt.title(titles[i])
plt.xticks([]),plt.yticks([])
plt.show()
As you can see in the resulting image, the problem with your image is the strong variation in background intensity. Without knowing the origin of your images, I'd start with one of the following two options before trying to solve this by software:
1.) Get a more uniform illumination
2.) If not possible, and if illumination is the same for all images, make an empty image of background only and subtract it from your other images, in order to get more uniform illumination.
It is important to understand that this is not a software issue: In the border areas, the background is really darker than the structure in positions near the middle of the image. You can check this by checking gray values of single pixels. Only your eye is very good in compensating for this. The adaptive thresholding techniques listed below try to find an appropriate local threshold, but there is a limit to this. You may be able to find more adequate parameters, though, but first thing I'd recommend is working on uniformity of illumination.
[EDIT]
I have added an additional bit of code below for playing around with the images which allows you to
.
import cv2
import numpy as np
mode = ""
Schwelle =0
def nothing(x):
pass
def read_pixel(event,x,y,flags,param):
global mode, Schwelle
if event == cv2.EVENT_MOUSEMOVE:
if mode == "Analyse":
ix,iy = x,y
font = cv2.FONT_HERSHEY_SIMPLEX
img2=img.copy()
cv2.putText(img2,str(ix)+" "+str(iy)+": "+str(img[y,x]),(50,50), font, 1,(255,255,255),2,cv2.LINE_AA)
cv2.imshow('image',img2)
elif event == cv2.EVENT_RBUTTONDOWN:
if mode == "Analyse":
mode=""
Schwelle=img[y,x]
cv2.createTrackbar('Schwelle','image',Schwelle,255,nothing)
else:
mode = "Analyse"
elif event == cv2.EVENT_LBUTTONDOWN:
Schwelle= img[y,x]
img =cv2.imread('image.png',0)
cv2.namedWindow('image')
cv2.setMouseCallback('image',read_pixel)
cv2.createTrackbar('Schwelle','image',128,255,nothing)
while(1):
k = cv2.waitKey(1) & 0xFF
if k == 27:
break
if mode != "Analyse":
Schwelle = cv2.getTrackbarPos('Schwelle','image')
ret,thresh1 = cv2.threshold(img,Schwelle,255,cv2.THRESH_BINARY)
cv2.imshow('image',thresh1)
cv2.destroyAllWindows()
Upvotes: 3