Increase image brightness without overflow

14,165

Solution 1

Here's my shot at a simple algorithm for cleaning up that particular image. Feel free to play with it and tweak it further to get the desired result.

NB: The code shown should work both with the 2.4.x and 3.x branches of OpenCV.

Step 0

Load the input image as grayscale.

img = cv2.imread('paper.jpg',0)

Step 1

Dilate the image, in order to get rid of the text. This step somewhat helps to preserve the bar code.

dilated_img = cv2.dilate(img, np.ones((7,7), np.uint8)) 

Dilated

Step 2

Median blur the result with a decent sized kernel to further suppress any text.

This should get us a fairly good background image that contains all the shadows and/or discoloration.

bg_img = cv2.medianBlur(dilated_img, 21)

Blurred

Step 3

Calculate the difference between the original and the background we just obtained. The bits that are identical will be black (close to 0 difference), the text will be white (large difference).

Since we want black on white, we invert the result.

diff_img = 255 - cv2.absdiff(img, bg_img)

Inverted Difference

Step 4

Normalize the image, so that we use the full dynamic range.

norm_img = diff_img.copy() # Needed for 3.x compatibility
cv2.normalize(diff_img, norm_img, alpha=0, beta=255, norm_type=cv2.NORM_MINMAX, dtype=cv2.CV_8UC1)

Normalized

Step 5

At this point we still have the paper somewhat gray. We can truncate that away, and re-normalize the image.

_, thr_img = cv2.threshold(norm_img, 230, 0, cv2.THRESH_TRUNC)
cv2.normalize(thr_img, thr_img, alpha=0, beta=255, norm_type=cv2.NORM_MINMAX, dtype=cv2.CV_8UC1)

Gray Trimmed

Done...

Well, at least for me ;) You will probably want to crop it, and do whatever other post-processing you desire.


Note: It might be worth switching to higher precision (16+ bit int or float) after you get the difference image, in order to minimize accumulating rounding errors in the repeated normalizations.

Solution 2

What you need is thresholding. This will allow you to set everything that isn't very dark to pure white, and set the very dark stuff to straight black (or any other colors). This should get you started:

cutoff_val = 100 # everything above this is set to set_color
set_color = 255 
ret,thresh_img = cv2.threshold(image,cutoff_val,set_color,cv2.THRESH_BINARY)

After you play around with that, you can get better results using adaptive thresholding.

See this great tutorial doing basically what you want---and bonus, it includes a tutorial on warping to get a rectangular region for the paper, too!

Edit: I got pretty good results with your image combining thresholding and then adaptive thresholding.

cutoff_val = 150 # everything above this is set to the cutoff val
set_color = 255 # if 
ret,thresh_img = cv2.threshold(image,cutoff_val,set_color,cv2.THRESH_TRUNC)
window_sz = 3
thresh_img2 = cv2.adaptiveThreshold(thresh_img,set_color,cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
            cv2.THRESH_BINARY,window_sz,2)
Share:
14,165

Related videos on Youtube

Wesley
Author by

Wesley

Updated on September 15, 2022

Comments

  • Wesley
    Wesley over 1 year

    I got a problem when trying to increase image brightness.

    Here is the origin image:

    enter image description here

    The image I wanted to get is like this:

    enter image description here

    Now to increase the brightness with the following code:

        image = cv2.imread("/home/wni/vbshare/tmp/a4_index2.png",0)
    
        if sum(image[0])/len(image[0])<200:
            new = np.where((255-image)<image,255,image*2)
        else:
            new = image
        return new
    

    And, I got the following image:

    enter image description here

    So, seems brightness of some points overflowed.

    And I tried to change the threshold from 200 to some other number, e.g. 125, 100, 140 .etc However, the image brightness stays either almost same dark or overflow.

    Env:

    Python: 2.7.10

    Opencv: 3.2.0

    Any suggestion for this is appreciated.

    Thanks.

    • ZdaR
      ZdaR almost 7 years
      You may use cv2.adaptiveThreshold() to get somewhat similar results but you may not be able to get the exact original form.
    • Dan Mašek
      Dan Mašek almost 7 years
      None of the attached images work.
    • Wesley
      Wesley almost 7 years
      You sure? It's OK for me
    • Dan Mašek
      Dan Mašek almost 7 years
      @Wesley Interesting. Must have been some temporary issue.
    • Dan Mašek
      Dan Mašek almost 7 years
      Anyway, to your problem -- to avoid the overflow I'd use an operation that saturates. e.g. cv2.add the source array to itself, using a mask to select which elements to update. That's not to say it's an ideal approach.
    • Wesley
      Wesley almost 7 years
      @DanMašek I just tried cv2.add seems the same behavior as my previous code
    • Dan Mašek
      Dan Mašek almost 7 years
      @Wesley In that case there's no overflow involved. Considering you're only doubling pixels with intensities below 128 ((255-image)<image simplifies to image < (255/2)), there isn't any opportunity for it.
  • Wesley
    Wesley almost 7 years
    Hi, I just have a try on your code, yeah, It get brighter, but also be glurry, I guess to be from cv2.ADAPTIVE_THRESH_GAUSSIAN_C, so I changed to cv2.ADAPTIVE_THRESH_MEAN_C, but with almost same result. Because I still need to do some ocr recognize after this, so, could you have suggestion on keep clarity when increasing brightness :-)
  • Wesley
    Wesley almost 7 years
    Hi, thanks for your response. I just have a try and get what I wanna. Will accept as answer.
  • mPrinC
    mPrinC almost 4 years
    Hi @DanMašek @dan-ma%c5%a1ek, could you provide some code example or explanation where to apply your comment: > Note: It might be worth switching to higher precision (16+ bit int or float) after you get the difference image, in order to minimize accumulating rounding errors in the repeated normalizations.