Search code examples
pythonopencvimage-processingimage-recognitionpython-tesseract

Extract text in multiple background from image


I have multiple image with different background,

i need to ignore background and extract Number from my image. ex:

Original

Original with different background

Original diff 3

after test, i have this result :

thresh

because of background color, it's very difficult to extract text..

i'm using this code:

image = cv2.imread('AA.png')

gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
thresh = cv2.threshold(gray, 165, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)[1]



# Invert image and perform morphological operations
inverted = 255 - thresh
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (15,3))
close = cv2.morphologyEx(inverted, cv2.MORPH_CLOSE, kernel, iterations=1)

# Find contours and filter using aspect ratio and area
cnts = cv2.findContours(close, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
for c in cnts:
    area = cv2.contourArea(c)
    peri = cv2.arcLength(c, True)
    approx = cv2.approxPolyDP(c, 0.01 * peri, True)
    x,y,w,h = cv2.boundingRect(approx)
    aspect_ratio = w / float(h)
    if (aspect_ratio >= 2.5 or area < 75):
        cv2.drawContours(thresh, [c], -1, (255,255,255), -1)

# Blur and perform text extraction
thresh = cv2.GaussianBlur(thresh, (3,3), 0)
data = pytesseract.image_to_string(thresh, lang='eng',config='tessedit_char_whitelist=0123456789 --psm 6')
print(data)


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

How i can extract number from this image with accuracy even if background color change?

Edit result after modification:

comment


Solution

  • Your thresholding is your problem. Here is how I would process the image in Python/OpenCV before doing OCR.

    I simply threshold at 165 to make the letters white and the background black. Then filter contours on area to remove the small extraneous white regions. Then invert the results so that you have black letters on white background.

    Input:

    enter image description here

    import cv2
    import numpy as np
    
    # load image as HSV and select saturation
    img = cv2.imread("numbers.png")
    hh, ww, cc = img.shape
    
    # convert to gray
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    
    # threshold the grayscale image
    ret, thresh = cv2.threshold(gray,165,255,0)
    
    # create black image to hold results
    results = np.zeros((hh,ww))
    
    # find contours
    cntrs = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    cntrs = cntrs[0] if len(cntrs) == 2 else cntrs[1]
    
    # Contour filtering and copy contour interior to new black image.
    for c in cntrs:
        area = cv2.contourArea(c)
        if area > 1000:
            x,y,w,h = cv2.boundingRect(c)
            results[y:y+h,x:x+w] = thresh[y:y+h,x:x+w]
    
    # invert the results image so that have black letters on white background
    results = (255 - results)
    
    # write results to disk
    cv2.imwrite("numbers_extracted.png", results)
    
    cv2.imshow("THRESH", thresh)
    cv2.imshow("RESULTS", results)
    cv2.waitKey(0)
    cv2.destroyAllWindows()
    


    Thresholded Image Before Contour Filtering:

    enter image description here

    Results after contour filtering and inversion:

    enter image description here

    P.S. cv2.inRange() may be an alternative to cv2.threshold.

    And of course this solution may be limited to this one image, since other images may need different values for the threshold and area limits.