'How to perform image lighting correction with OpenCV?

I have an image which I grab using a camera. Sometimes, the lighting is uneven in them image. There are some dark shades. This causes incorrect optimal thresholding in EMGU as well as Aforge to process the image for OCR.

This is the image:

enter image description here

This is what I get after thresholding:

enter image description here

How do I correct the lighting? I tried adaptive threshold, gives about the same result. Tried gamma correction too using the code below:

 ImageAttributes attributes = new ImageAttributes();
            attributes.SetGamma(10);

            // Draw the image onto the new bitmap
            // while applying the new gamma value.
            System.Drawing.Point[] points =
   {
    new System.Drawing.Point(0, 0),
    new System.Drawing.Point(image.Width, 0),
    new System.Drawing.Point(0, image.Height),
   };
            Rectangle rect =
                new Rectangle(0, 0, image.Width, image.Height);

            // Make the result bitmap.
            Bitmap bm = new Bitmap(image.Width, image.Height);
            using (Graphics gr = Graphics.FromImage(bm))
            {
                gr.DrawImage(HSICONV.Bitmap, points, rect,
                    GraphicsUnit.Pixel, attributes);
            }

same result. Please help.

UPDATE: as per Nathancy's suggestion I converted his code to c# for uneven lighting correction and it works:

   Image<Gray, byte> smoothedGrayFrame = grayImage.PyrDown();
                smoothedGrayFrame = smoothedGrayFrame.PyrUp();
                //canny
                Image<Gray, byte> cannyFrame = null;

                cannyFrame = smoothedGrayFrame.Canny(50, 50);
                //smoothing

                grayImage = smoothedGrayFrame;
                //binarize
                Image<Gray, byte> grayout = grayImage.Clone();
                CvInvoke.AdaptiveThreshold(grayImage, grayout, 255, AdaptiveThresholdType.GaussianC, ThresholdType.BinaryInv, Convert.ToInt32(numericmainthreshold.Value) + Convert.ToInt32(numericmainthreshold.Value) % 2 + 1, 1.2d);
                grayout._Not();
                Mat kernelCl = CvInvoke.GetStructuringElement(ElementShape.Rectangle, new Size(3, 3), new System.Drawing.Point(-1, -1));
                CvInvoke.MorphologyEx(grayout, grayout, MorphOp.Close, kernelCl, new System.Drawing.Point(-1, -1), 1, BorderType.Default, new MCvScalar());


Solution 1:[1]

Here's an approach:

  • Convert image to grayscale and Gaussian blur to smooth image
  • Adaptive threshold to obtain binary image
  • Perform morphological transformations to smooth image
  • Dilate to enhance text
  • Invert image

After converting to grayscale and blurring, we adaptive threshold

There are small holes and imperfections so we perform a morph close to smooth the image

From we here can optionally dilate to enhance the text

Now we invert the image to get our result

I implemented this method in OpenCV and Python but you can adapt the same strategy into C#

import cv2

image = cv2.imread('1.png')
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
blur = cv2.GaussianBlur(gray, (5,5), 0)
thresh = cv2.adaptiveThreshold(blur,255,cv2.ADAPTIVE_THRESH_GAUSSIAN_C, \
         cv2.THRESH_BINARY_INV,9,11)
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3,3))
close = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, kernel)
dilate = cv2.dilate(close, kernel, iterations=1)
result = 255 - dilate 

cv2.imshow('thresh', thresh)
cv2.imshow('close', close)
cv2.imshow('dilate', dilate)
cv2.imshow('result', result)
cv2.waitKey()

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1