How do I increase the contrast of an image in Python OpenCV

164,025

Solution 1

I would like to suggest a method using the LAB color space.

LAB color space expresses color variations across three channels. One channel for brightness and two channels for color:

  • L-channel: representing lightness in the image
  • a-channel: representing change in color between red and green
  • b-channel: representing change in color between yellow and blue

In the following I perform adaptive histogram equalization on the L-channel and convert the resulting image back to BGR color space. This enhances the brightness while also limiting contrast sensitivity. I have done the following using OpenCV 3.0.0 and python:

Code:

import cv2
import numpy as np

img = cv2.imread('flower.jpg', 1)
# converting to LAB color space
lab= cv2.cvtColor(img, cv2.COLOR_BGR2LAB)
l_channel, a, b = cv2.split(lab)

# Applying CLAHE to L-channel
# feel free to try different values for the limit and grid size:
clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8,8))
cl = clahe.apply(l_channel)

# merge the CLAHE enhanced L-channel with the a and b channel
limg = cv2.merge((cl,a,b))

# Converting image from LAB Color model to BGR color spcae
enhanced_img = cv2.cvtColor(limg, cv2.COLOR_LAB2BGR)

# Stacking the original image with the enhanced image
result = np.hstack((img, enhanced_img))
cv2.imshow('Result', result)

Result:

The enhanced image is on the right

enter image description here

You can run the code as it is. To know what CLAHE (Contrast Limited Adaptive Histogram Equalization) is about, refer this Wikipedia page

Solution 2

For Python, I haven't found an OpenCV function that provides contrast. As others have suggested, there are some techniques to automatically increase contrast using a very simple formula.

In the official OpenCV docs, it is suggested that this equation can be used to apply both contrast and brightness at the same time:

new_img = alpha*old_img + beta

where alpha corresponds to a contrast and beta is brightness. Different cases

alpha 1  beta 0      --> no change  
0 < alpha < 1        --> lower contrast  
alpha > 1            --> higher contrast  
-127 < beta < +127   --> good range for brightness values

In C/C++, you can implement this equation using cv::Mat::convertTo, but we don't have access to that part of the library from Python. To do it in Python, I would recommend using the cv::addWeighted function, because it is quick and it automatically forces the output to be in the range 0 to 255 (e.g. for a 24 bit color image, 8 bits per channel). You could also use convertScaleAbs as suggested by @nathancy.

import cv2
img = cv2.imread('input.png')
# call addWeighted function. use beta = 0 to effectively only operate one one image
out = cv2.addWeighted( img, contrast, img, 0, brightness)
output = cv2.addWeighted

The above formula and code is quick to write and will make changes to brightness and contrast. But they yield results that are significantly different than photo editing programs. The rest of this answer will yield a result that will reproduce the behavior in the GIMP and also LibreOffice brightness and contrast. It's more lines of code, but it gives a nice result.

Contrast

In the GIMP, contrast levels go from -127 to +127. I adapted the formulas from here to fit in that range.

f = 131*(contrast + 127)/(127*(131-contrast))
new_image = f*(old_image - 127) + 127 = f*(old_image) + 127*(1-f)

To figure out brightness, I figured out the relationship between brightness and levels and used information in this levels post to arrive at a solution.

#pseudo code
if brightness > 0
    shadow = brightness
    highlight = 255
else:
    shadow = 0
    highlight = 255 + brightness
new_img = ((highlight - shadow)/255)*old_img + shadow

brightness and contrast in Python and OpenCV

Putting it all together and adding using the reference "mandrill" image from USC SIPI:

import cv2
import numpy as np

# Open a typical 24 bit color image. For this kind of image there are
# 8 bits (0 to 255) per color channel
img = cv2.imread('mandrill.png')  # mandrill reference image from USC SIPI

s = 128
img = cv2.resize(img, (s,s), 0, 0, cv2.INTER_AREA)

def apply_brightness_contrast(input_img, brightness = 0, contrast = 0):
    
    if brightness != 0:
        if brightness > 0:
            shadow = brightness
            highlight = 255
        else:
            shadow = 0
            highlight = 255 + brightness
        alpha_b = (highlight - shadow)/255
        gamma_b = shadow
        
        buf = cv2.addWeighted(input_img, alpha_b, input_img, 0, gamma_b)
    else:
        buf = input_img.copy()
    
    if contrast != 0:
        f = 131*(contrast + 127)/(127*(131-contrast))
        alpha_c = f
        gamma_c = 127*(1-f)
        
        buf = cv2.addWeighted(buf, alpha_c, buf, 0, gamma_c)

    return buf


font = cv2.FONT_HERSHEY_SIMPLEX
fcolor = (0,0,0)

blist = [0, -127, 127,   0,  0, 64] # list of brightness values
clist = [0,    0,   0, -64, 64, 64] # list of contrast values


out = np.zeros((s*2, s*3, 3), dtype = np.uint8)

for i, b in enumerate(blist):
    c = clist[i]
    print('b, c:  ', b,', ',c)
    row = s*int(i/3)
    col = s*(i%3)
    
    print('row, col:   ', row, ', ', col)
    
    out[row:row+s, col:col+s] = apply_brightness_contrast(img, b, c)
    msg = 'b %d' % b
    cv2.putText(out,msg,(col,row+s-22), font, .7, fcolor,1,cv2.LINE_AA)
    msg = 'c %d' % c
    cv2.putText(out,msg,(col,row+s-4), font, .7, fcolor,1,cv2.LINE_AA)
    
    cv2.putText(out, 'OpenCV',(260,30), font, 1.0, fcolor,2,cv2.LINE_AA)

cv2.imwrite('out.png', out)

enter image description here

I manually processed the images in the GIMP and added text tags in Python/OpenCV:
enter image description here

Note: @UtkarshBhardwaj has suggested that Python 2.x users must cast the contrast correction calculation code into float for getting floating result, like so:

...
if contrast != 0:
        f = float(131*(contrast + 127))/(127*(131-contrast))
...

Solution 3

Contrast and brightness can be adjusted using alpha (α) and beta (β), respectively. These variables are often called the gain and bias parameters. The expression can be written as

enter image description here

OpenCV already implements this as cv2.convertScaleAbs(), just provide user defined alpha and beta values

import cv2

image = cv2.imread('1.jpg')

alpha = 1.5 # Contrast control (1.0-3.0)
beta = 0 # Brightness control (0-100)

adjusted = cv2.convertScaleAbs(image, alpha=alpha, beta=beta)

cv2.imshow('original', image)
cv2.imshow('adjusted', adjusted)
cv2.waitKey()

Before -> After

enter image description here enter image description here

Note: For automatic brightness/contrast adjustment take a look at automatic contrast and brightness adjustment of a color photo

Solution 4

Best explanation for X = aY + b (in fact it f(x) = ax + b)) is provided at https://math.stackexchange.com/a/906280/357701

A Simpler one by just adjusting lightness/luma/brightness for contrast as is below:

import cv2

img = cv2.imread('test.jpg')
cv2.imshow('test', img)
cv2.waitKey(1000)
imghsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)


imghsv[:,:,2] = [[max(pixel - 25, 0) if pixel < 190 else min(pixel + 25, 255) for pixel in row] for row in imghsv[:,:,2]]
cv2.imshow('contrast', cv2.cvtColor(imghsv, cv2.COLOR_HSV2BGR))
cv2.waitKey(1000)
raw_input()

Solution 5

img = cv2.imread("/x2.jpeg")

image = cv2.resize(img, (1800, 1800))

alpha=1.5
beta=20

new_image=cv2.addWeighted(image,alpha,np.zeros(image.shape, image.dtype),0,beta)

cv2.imshow("new",new_image)
cv2.waitKey(0)
cv2.destroyAllWindows()
Share:
164,025
tsaebeht
Author by

tsaebeht

Updated on July 09, 2022

Comments

  • tsaebeht
    tsaebeht almost 2 years

    I am new to Python OpenCV. I have read some documents and answers here but I am unable to figure out what the following code means:

    if (self.array_alpha is None):
        self.array_alpha = np.array([1.25])
        self.array_beta = np.array([-100.0])
    
    # add a beta value to every pixel 
    cv2.add(new_img, self.array_beta, new_img)                    
    
    # multiply every pixel value by alpha
    cv2.multiply(new_img, self.array_alpha, new_img)  
    

    I have come to know that Basically, every pixel can be transformed as X = aY + b where a and b are scalars.. Basically, I have understood this. However, I did not understand the code and how to increase contrast with this.

    Till now, I have managed to simply read the image using img = cv2.imread('image.jpg',0)

    Thanks for your help

  • Jeremy Caney
    Jeremy Caney almost 4 years
    There are some really good, detailed answers with a lot of upvotes on this question. It's fine to add another answer if the existing answers are missing something. But given how detailed they are, and how much they've been validated by the community, it'd be really useful to update your answer to explain what you're doing, and why you see it as an improvement over those existing answers. Would you mind doing that?
  • Coder
    Coder almost 4 years
    @JeremyCaney I Added, because It is very straightforward and people make it complicated, Just few line of VERY SIMPLE code, and we get our expected result, if it's wrong, then I can remove it!
  • bfris
    bfris almost 4 years
    @Nykodym, you were completely right about your comment. The Answer has been significantly edited to be more consistent.
  • Nykodym
    Nykodym almost 4 years
    Awesome, much better :) Deleted the old, out-of-the-context comment and +1.
  • idontknow
    idontknow over 3 years
    Yes, thanks so much for making it so simple for me! Loved it!
  • bfris
    bfris over 2 years
    In addition to Wikipedia, the Python tutorials for OpenCV have a nice section on CLAHE
  • pietz
    pietz over 2 years
    When the image data isn't zero centered (and that's usually the case in OpenCV) adjusting alpha does not correspond to changing only the contrast. You show this quite clearly with the example. The modified image is brighter although it appears you only wanted to change the contrast. I added an answer myself that shifts the brightness according to the contrast chosen.
  • Jason S
    Jason S over 2 years
    convertScaleAbs should not be used in this way, because if the resulting values are ever negative, they don't saturate at zero, instead an absolute value is applied.
  • Cristiano Coelho
    Cristiano Coelho over 2 years
    This was actually the most helpful question as the others were missing the brightness adjustment if you wanted to keep it untouched.
  • Admin
    Admin almost 2 years
    Can you add the code for defining a and b in cv2.merge((cl,a,b)) please?
  • Jeru Luke
    Jeru Luke almost 2 years
    @WolfgangSpindeler I have added the line l_channel, a, b = cv2.split(lab)
  • Giacomo1968
    Giacomo1968 almost 2 years
    @Coder Nobody said you should remove this. But perhaps add a simple explanatory text to explain why this simple example works just as well as other examples. That’s it.
  • Giacomo1968
    Giacomo1968 almost 2 years
    @CristianoCoelho Agreed! I already have a process that adjusts contrast but never adjusted brightness in the context of contrast. The core line of brightness += int(round(255*(1-contrast)/2)) has helped me immensely. Thanks!