0

So I am running a bit of code that is supposed to allow me to desaturate and then saturate an image. I have a function that is supposed to allow me to do this, but no matter how I change the code, it always shifts the color so it doesn't desaturate.

My Function:

def desaturation(img,percent=.5):
    imgGreen=img[:,:,1]
    desatGreen=imgGreen*percent
    desatImg=img[:,:,:]
    desatImg[:,:,1]=desatGreen
    return desatImg

Each time I run it, it shifts the color to a magenta rather than a desaturated look that I would expect. I would prefer to have the color range still remain in BGR rather than any of the other color ranges. Is there any bit of code that can help me with this, doing it raw rather than relying on another builtin function?

Edit: I have the percent variable set so that it can never go above 1 and never below 0.

Edit: I would like to desaturate the entire image. The way my code is written is a bit misleading.

fmw42
  • 46,825
  • 10
  • 62
  • 80
Michael Pate
  • 89
  • 1
  • 9
  • you should use HUV to change saturate but not RGB,and there is many method in google search"python opencv saturation" – Zhubei Federer Nov 10 '20 at 02:33
  • What are you expecting? Desaturating green will emphasize red and blue more. So for what you are trying, you are getting the proper result. – fmw42 Nov 10 '20 at 03:59

2 Answers2

2

Here is one simple way to desaturate green in Python OpenCV. The way to do that is to convert to CMYK and then desaturate the C and Y channels. Unfortunately, OpenCV does not have a BGR2CMYK color conversion built-in. So one needs to do the computations. Also this method affects the other colors as well.

Input:

enter image description here

import cv2
import numpy as np

img = cv2.imread("barn.jpg")
scale = 255
percent = 0.5
#percent = 0.25
#percent = 0

# separate b,g,r
b,g,r = cv2.split(img)
b = b.astype(np.float32)
g = g.astype(np.float32)
r = r.astype(np.float32)

# convert to cmyk
# see 
# https://stackoverflow.com/questions/14088375/how-can-i-convert-rgb-to-cmyk-and-vice-versa-in-python/41220097
# https://www.codeproject.com/Articles/4488/XCmyk-CMYK-to-RGB-Calculator-with-source-code
c = 1 - r / scale
m = 1 - g / scale
y = 1 - b / scale
k = cv2.min(cv2.min(c, m),y)
c = scale * (c - k) / (1 - k)
m = scale * (m - k) / (1 - k)
y = scale * (y - k) / (1 - k)

# desaturate neighbors of G which are C,Y
c = cv2.multiply(c, percent)
y = cv2.multiply(y, percent)

# convert back to bgr
r = scale * (1.0 - c / scale) * (1.0 - k)
g = scale * (1.0 - m / scale) * (1.0 - k)
b = scale * (1.0 - y / scale) * (1.0 - k)
r = r.clip(0,255).astype(np.uint8)
g = g.clip(0,255).astype(np.uint8)
b = b.clip(0,255).astype(np.uint8)
img_desat = cv2.merge([b,g,r])

# save result
cv2.imwrite('barn_desat_0p5.jpg', img_desat)
#cv2.imwrite('barn_desat_0p25.jpg', img_desat)
#cv2.imwrite('barn_desat_0.jpg', img_desat)

cv2.imshow('img', img)
cv2.imshow('img_desat', img_desat)
cv2.waitKey(0)
cv2.destroyAllWindows()

Desaturate to 50%:

enter image description here

Desaturate to 25%:

enter image description here

Desaturate to 0:

enter image description here

fmw42
  • 46,825
  • 10
  • 62
  • 80
2

The proper way to desaturate a color (green) in Python OpenCV is to work in the HSV color space.

  • Read the input
  • Convert to HSV and separate channels
  • Desaturate the S channel
  • Merge back with original H and V channels
  • Convert back to BGR
  • Create a 1D LUT image that is black everywhere except white where green would be
  • Apply the LUT to the H channel and save as a mask in the range 0 to 1
  • Create a blended image that is the desaturated image where the mask is white (corresponding to green) and original image where the mask is black (other than green)
  • Save as the result

Input:

enter image description here

import cv2
import numpy as np

img = cv2.imread("barn.jpg")
percent = 0.5
#percent = 0.25
#percent = 0

# convert to HSV
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
h,s,v = cv2.split(hsv)

# desaturate
s_desat = cv2.multiply(s, percent).astype(np.uint8)
hsv_new = cv2.merge([h,s_desat,v])
bgr_desat = cv2.cvtColor(hsv_new, cv2.COLOR_HSV2BGR)

# create 1D LUT for green
# (120 out of 360) = (60 out of 180)  +- 25
lut = np.zeros((1,256), dtype=np.uint8)
white = np.full((1,50), 255, dtype=np.uint8)
lut[0:1, 35:85] = white
print(lut.shape, lut.dtype)

# apply lut to hue channel as mask
mask = cv2.LUT(h, lut)
mask = mask.astype(np.float32) / 255
mask = cv2.merge([mask,mask,mask])

# mask bgr_desat and img
result = mask * bgr_desat + (1 - mask)*img
result = result.clip(0,255).astype(np.uint8)

# save result
cv2.imwrite('barn_desat2_0p5.jpg', result)
#cv2.imwrite('barn_desat2_0p25.jpg', result)
#cv2.imwrite('barn_desat2_0.jpg', result)

cv2.imshow('img', img)
cv2.imshow('bgr_desat', bgr_desat)
cv2.imshow('result', result)
cv2.waitKey(0)
cv2.destroyAllWindows()

Desaturate green 50%:

enter image description here

Desaturate green 25%:

enter image description here

Desaturate green 0:

enter image description here

fmw42
  • 46,825
  • 10
  • 62
  • 80