Fariss Abdo
Fariss Abdo

Reputation: 501

OpenCV Python equalizeHist colored image

I need to do a histogram equalization for a colored image.

First I convert the colored image to gray and give it to the equalizeHist function:

image = cv2.imread("photo.jpg")
image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
cv2.equalizeHist(image)
cv2.imshow("equalizeHist", image)
cv2.waitKey(0)

But after this I need to convert the image back to RGB; how can i do that?

Upvotes: 38

Views: 92529

Answers (11)

Andy
Andy

Reputation: 381

The code above renders the use of histogram equalization in different color spaces.

import cv2
import matplotlib.pyplot as plt
import math

img = cv2.imread('../pix/Lenna.png')

def contrast(img, color_space, channels):
    img_temp = img if color_space == 'BGR' else cv2.cvtColor(img, eval(f"cv2.COLOR_BGR2{color_space}"))
    img_temp = cv2.merge([cv2.equalizeHist(c) if i in channels else c for i, c in enumerate(cv2.split(img_temp))])
    return img_temp if color_space == 'RGB' else cv2.cvtColor(img_temp, eval(f"cv2.COLOR_{color_space}2RGB"))

# a dictionary with the names of the color spaces to which the image needs to be converted and the channel numbers to be converted
spaces = {'BGR': [], 'GRAY': [0], 'RGB': [0, 1, 2], 'HSV': [2], 'HLS': [1], 'YCrCb': [0], 'Lab': [0], 'Luv': [0], 'YUV': [0]}

fig = plt.figure(figsize = (15, 15))

cols = 3
rows = math.ceil(len(spaces) / cols)

for i, s in enumerate(spaces):
    fig.add_subplot(rows, cols, i+1)
    plt.imshow(contrast(img, s, spaces[s]));
    # what color space has been converted to and what channel numbers have been changed
    plt.title(f"BGR → {s} {spaces[s]} → RGB") 
plt.show()

Applying equalizeHist across different color spaces

Upvotes: 0

Aish
Aish

Reputation: 1

you should not convert the image in to gray scale. you can do it with the colored image. but always OpenCV read the image with BGR color format. not in RGB format

import cv2

# imread needs 2 parameters. path and the flag
img = cv2.imread("photo.jpg", 1)

# convert image from BGR to HSV
img_hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)

# Histogram equalisation on the V-channel
img_hsv[:, :, 2] = cv2.equalizeHist(img_hsv[:, :, 2])

# now the img_hsv has equalized
# now convert hsv image back from HSV to RGB
imgequalized = cv2.cvtColor(img_hsv, cv2.COLOR_HSV2RGB)

cv2.imshow("original img", img)
cv2.imshow("equalizeHist", imgequalized)
cv2.waitKey(0)

Upvotes: 0

Minhas Kamal
Minhas Kamal

Reputation: 22126

input image vs. equalized image


Python Code

import cv2

def run_histogram_equalization(image_path):
    rgb_img = cv2.imread(image_path)

    # convert from RGB color-space to YCrCb
    ycrcb_img = cv2.cvtColor(rgb_img, cv2.COLOR_BGR2YCrCb)

    # equalize the histogram of the Y channel
    ycrcb_img[:, :, 0] = cv2.equalizeHist(ycrcb_img[:, :, 0])

    # convert back to RGB color-space from YCrCb
    equalized_img = cv2.cvtColor(ycrcb_img, cv2.COLOR_YCrCb2BGR)

    cv2.imshow('equalized_img', equalized_img)
    cv2.waitKey(0)

Explanation

Histogram Equalization (HE) is a statistical approach for spreading out intensity values. In image processing, HE is used for improving the contrast of any image, that is- to make the dark portion darker and the bright portion brighter.

For a grey-scale image, each pixel is represented by the intensity value (brightness); that is why we can feed the pixel values directly to the HE function. However, that is not how it works for an RGB-formatted color image. Each channel of the R, G, and B represents the intensity of the related color, not the intensity/brightness of the image as a whole. And so, running HE on these color channels is NOT the proper way.

We should first separate the brightness of the image from the color and then run HE on the brightness. Now, there are already standardized colorspaces that encode brightness and color separately, like- YCbCr, HSV, etc.; so, we can use them here for separating and then re-merging the brightness. The proper way:

Convert the colorspace from RGB to YCbCr >> Run HE on the Y channel (this channel represents brightness) >> Convert back the colorspace to RGB


Postscript

For HSV colorspace, HE should be run on the V channel. However, the Y channel of YCbCr is the better representer for brightness than the V channel of HSV. So, using the YCbCr format produces a more correct result for HE.

HSV vs YCbCr


Postscript 2

HE is too a naive technique and often produces peculiar colors and small artifacts. This is because it does not care about outliers and the location of a pixel. So, extensions like- ‎Contrast Limited Adaptive HE, Brightness preserving Bi-HE, etc. are used more commonly. Also, different noise reduction functions are executed in the post-processing phase for improving the final output.

Upvotes: 26

Er Anjali
Er Anjali

Reputation: 41

The color conversion method cv2.cvtColor() is used to convert the original image in between RGB/BGR and YUV. Here is the best coding snippet -

# convert it to grayscale
img_yuv = cv2.cvtColor(img,cv2.COLOR_BGR2YUV)

# apply histogram equalization 
img_yuv[:,:,0] = cv2.equalizeHist(img_yuv[:,:,0])
hist_eq = cv2.cvtColor(img_yuv, cv2.COLOR_YUV2BGR)

You can know more on this from here - https://www.etutorialspoint.com/index.php/311-python-opencv-histogram-equalization

Upvotes: 4

Kumar Vaibhav
Kumar Vaibhav

Reputation: 71

Here is a function which would take color image as input and will return the histogram equalize image.

# function for color image equalization
def histogram_equalization(img_in):
    # segregate color streams
    b, g, r = cv2.split(img_in)
    h_b, bin_b = np.histogram(b.flatten(), 256, [0, 256])
    h_g, bin_g = np.histogram(g.flatten(), 256, [0, 256])
    h_r, bin_r = np.histogram(r.flatten(), 256, [0, 256])
    # calculate cdf
    cdf_b = np.cumsum(h_b)
    cdf_g = np.cumsum(h_g)
    cdf_r = np.cumsum(h_r)

    # mask all pixels with value=0 and replace it with mean of the pixel values
    cdf_m_b = np.ma.masked_equal(cdf_b, 0)
    cdf_m_b = (cdf_m_b - cdf_m_b.min()) * 255 / (cdf_m_b.max() - cdf_m_b.min())
    cdf_final_b = np.ma.filled(cdf_m_b, 0).astype('uint8')

    cdf_m_g = np.ma.masked_equal(cdf_g, 0)
    cdf_m_g = (cdf_m_g - cdf_m_g.min()) * 255 / (cdf_m_g.max() - cdf_m_g.min())
    cdf_final_g = np.ma.filled(cdf_m_g, 0).astype('uint8')


    cdf_m_r = np.ma.masked_equal(cdf_r, 0)
    cdf_m_r = (cdf_m_r - cdf_m_r.min()) * 255 / (cdf_m_r.max() - cdf_m_r.min())
    cdf_final_r = np.ma.filled(cdf_m_r, 0).astype('uint8')
    # merge the images in the three channels
    img_b = cdf_final_b[b]
    img_g = cdf_final_g[g]
    img_r = cdf_final_r[r]

    img_out = cv2.merge((img_b, img_g, img_r))
    # validation
    equ_b = cv2.equalizeHist(b)
    equ_g = cv2.equalizeHist(g)
    equ_r = cv2.equalizeHist(r)
    equ = cv2.merge((equ_b, equ_g, equ_r))
    # print(equ)
    # cv2.imwrite('output_name.png', equ)
    return img_out

Upvotes: 2

Homura Akemi
Homura Akemi

Reputation: 51

img_yuv = cv2.cvtColor(image, cv2.COLOR_BGR2YUV)
clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(2,2))
img_yuv[:,:,0] = clahe.apply(img_yuv[:,:,0])
img = cv2.cvtColor(img_yuv, cv2.COLOR_YUV2BGR)
cv2.imshow("equalizeHist", img)
cv2.waitKey(0)

Upvotes: 5

sammens19
sammens19

Reputation: 331

You do not have to first convert your image to grayscale. You can use the approach below. A suggested solution above used the YUV colour space but I will do this example using the HSV colour space.

image = cv2.imread("photo.jpg")

# convert image from RGB to HSV
img_hsv = cv2.cvtColor(image, cv2.COLOR_RGB2HSV)

# Histogram equalisation on the V-channel
img_hsv[:, :, 2] = cv2.equalizeHist(img_hsv[:, :, 2])

# convert image back from HSV to RGB
image = cv2.cvtColor(img_hsv, cv2.COLOR_HSV2RGB)

cv2.imshow("equalizeHist", image)
cv2.waitKey(0)

Upvotes: 5

Mohammad Al Jazaery
Mohammad Al Jazaery

Reputation: 779

Source : https://www.packtpub.com/packtlib/book/Application-Development/9781785283932/2/ch02lvl1sec26/Enhancing%20the%20contrast%20in%20an%20image

import cv2
import numpy as np

img = cv2.imread('input.jpg')

img_yuv = cv2.cvtColor(img, cv2.COLOR_BGR2YUV)

# equalize the histogram of the Y channel
img_yuv[:,:,0] = cv2.equalizeHist(img_yuv[:,:,0])

# convert the YUV image back to RGB format
img_output = cv2.cvtColor(img_yuv, cv2.COLOR_YUV2BGR)

cv2.imshow('Color input image', img)
cv2.imshow('Histogram equalized', img_output)

cv2.waitKey(0)

~edit: original link is no longer available, similar idea is implemented here: Histogram Equalization of a Color image with OpenCV

Upvotes: 53

AFocsA
AFocsA

Reputation: 131

A more general approach would be transforming RGB values into another space that contains a luminescence/intensity value (Luv, Lab, HSV, HSL), apply histeq only in intensity plane and perform the inverse transform.

Upvotes: 12

Sanjay Chan
Sanjay Chan

Reputation: 589

If u want to equalizeHist the RGB image, u should not convert to gray instead of equalize RGB channels one by one.

So, i think maybe here is what u want:

def equalize_hist(img):
    for c in xrange(0, 2):
       img[:,:,c] = cv2.equalizeHist(img[:,:,c])

    cv2.imshow('Histogram equalized', img)
    cv2.waitKey(0)

    return img

Upvotes: 1

ehsan
ehsan

Reputation: 787

i'm not sure that it works properly :

def histogram_equalize(img):
    b, g, r = cv2.split(img)
    red = cv2.equalizeHist(r)
    green = cv2.equalizeHist(g)
    blue = cv2.equalizeHist(b)
    return cv2.merge((blue, green, red))

Upvotes: 1

Related Questions