2

I am trying to OCR the picture of documents and my current approach is

  1. Read an image as a grayscale
  2. Binarize it thresholding
  3. Wrap perspective along the contours obtained from cv2.findContours()

The above works well if image is not shadowed. Now I want to get contours of shadowed pictures. My first attempt is to use cv2.adaptiveThreshold for step 2. The adaptive threshold successfully weakened the shadow but the resulted image lost the contrast between the paper and the background. That made cv2 impossible to find contours of the paper. So I need to use other method to remove the shadow.

Is there any way to remove shadow maintaining the background colour?

For reference here is the sample picture I am processing with various approaches. From left, I did

  1. grayscale
  2. thresholding
  3. adaptive thresholdin
  4. normalization

My goal is to obtain the second picture without shadow.

enter image description here

Please note that I actually have a temporary solution specifically to the picture which is to process the part of the picture with shadow separately. Yet, it is not the general solution to shadowed picture as its performance depends on the size, shape and position of a shadow so please use other methods.

This is the original picture.

enter image description here

TFC
  • 466
  • 1
  • 5
  • 14

1 Answers1

5

Here is one way in Python/OpenCV using division normalization, optionally followed by sharpening and/or thresholding.

Input:

enter image description here

import cv2
import numpy as np
import skimage.filters as filters

# read the image
img = cv2.imread('receipt.jpg')

# convert to gray
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)

# blur
smooth = cv2.GaussianBlur(gray, (95,95), 0)

# divide gray by morphology image
division = cv2.divide(gray, smooth, scale=255)


# sharpen using unsharp masking
sharp = filters.unsharp_mask(division, radius=1.5, amount=1.5, multichannel=False, preserve_range=False)
sharp = (255*sharp).clip(0,255).astype(np.uint8)

# threshold
thresh = cv2.threshold(sharp, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)[1]

# save results
cv2.imwrite('receipt_division.png',division)
cv2.imwrite('receipt_division_sharp.png',sharp)
cv2.imwrite('receipt_division_thresh.png',thresh)


# show results
cv2.imshow('smooth', smooth)  
cv2.imshow('division', division)  
cv2.imshow('sharp', sharp)  
cv2.imshow('thresh', thresh)  
cv2.waitKey(0)
cv2.destroyAllWindows()

Division:

enter image description here

Sharpened:

enter image description here

Thresholded:

enter image description here

fmw42
  • 46,825
  • 10
  • 62
  • 80