简体   繁体   中英

Convert different shades of a color in an image to one color

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.

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

  • Adapt the threshold with a slider
  • Use the right mouse botton to see the original image again, inspect gray values at different mouse positions, and by clicking the right mouse button again, change back to threshold view and use gray value of last mouse position as new threshold value.

.

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()

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM