easyocr detects no integer values

I’m trying to detects the numbers found in my sqares, and I thought I could use the libary easyocr, but for some reason I read the wrong values.

This is the console output: console

And here I have all my pictures (they are seperated, this is just to show them all) boxes ¨ How can it be that i don’t find any numbers?

from PIL import Image
from operator import itemgetter
import numpy as np 
import easyocr
import cv2 
import re
import imutils
import pytesseract
pytesseract.pytesseract.tesseract_cmd = r'C:Program FilesTesseract-OCRtesseract'
reader = easyocr.Reader(['ch_sim','en']) # need to run only once to load model into memory

#Define empty array
Cubes = []

def getNumber(ROI):
    img = cv2.imread(ROI)
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    ret,thresh = cv2.threshold(gray,127,255,0)

    #cv2.imshow(thresh)
    #cv2.imshow('Thresholded original',thresh)
    #cv2.waitKey(0)

    ## Get contours
    contours,h = cv2.findContours(thresh,cv2.RETR_CCOMP, cv2.CHAIN_APPROX_SIMPLE)


    ## only draw contour that have big areas
    imx = img.shape[0]
    imy = img.shape[1]
    lp_area = (imx * imy) / 10

    tmp_img = img.copy()

    for cnt in contours:
        approx = cv2.approxPolyDP(cnt,0.01 * cv2.arcLength(cnt, True), True)
        
        if cv2.contourArea(cnt) > lp_area:

            # Draw box corners and minimum area rectangle
            rect = cv2.minAreaRect(cnt)
            box = cv2.boxPoints(rect)
            box = np.int0(box)
            #cv2.drawContours(tmp_img, [box], 0, (0, 50, 255), 3)
            #cv2.circle(tmp_img, tuple(box[0]), 8, (0, 255, 0), -1)
            #cv2.circle(tmp_img, tuple(box[1]), 8, (0, 255, 0), -1)
            #cv2.circle(tmp_img, tuple(box[2]), 8, (0, 255, 0), -1)
            #cv2.circle(tmp_img, tuple(box[3]), 8, (0, 255, 0), -1)
            #cv2.imshow(tmp_img)
            #cv2.imshow('Minimum Area Rectangle', tmp_img)
            #cv2.waitKey(0)

            ## Correct orientation and crop
            # Link, https://jdhao.github.io/2019/02/23/crop_rotated_rectangle_opencv/
            width = int(rect[1][0])
            height = int(rect[1][1])
            src_pts = box.astype("float32")
            dst_pts = np.array([[0, height-1],
                                [0, 0],
                                [width-1, 0],
                                [width-1, height-1]], dtype="float32")
            M = cv2.getPerspectiveTransform(src_pts, dst_pts)
            warped = cv2.warpPerspective(img, M, (width, height))


            # Run OCR on cropped image
            # If the predicted value is digit print else rotate first
            result = reader.readtext(warped)
            print(result)
            predicted_digit = result[0][1]

            if np.char.isdigit(predicted_digit) == True:
                cv2.imshow("warped " + ROI,warped)
            else: 
                rot_img = warped.copy()
                for i in range(0, 3):
                    rotated_image = cv2.rotate(rot_img, cv2.cv2.ROTATE_90_CLOCKWISE)
                    result = reader.readtext(rotated_image)
                    #if np.array(result).size == 0: 
                    #  continue
                    if not result:
                        rot_img = rotated_image 
                        continue
                    #if len(result) == 0:
                    #  continue
                    predicted_digit = result[0][1]
                    #print(result)
                    #print(predicted_digit)
                    #cv2.imshow(rotated_image)
                    if np.char.isdigit(predicted_digit) == True:
                        cv2.imshow("Image " + ROI, rotated_image)
                        break
                    rot_img = rotated_image                

    return predicted_digit

def sortNumbers(Cubes):

    Cubes = sorted(Cubes, key=lambda x: int(x[2]))

    #Cubes.sort(key=itemgetter(2))  # In-place sorting
    #Cubes = sorted(Cubes, key=itemgetter(2))  # Create a new list

    return Cubes
        

#img = cv2.imread('gulRecNum.jpg') 
img = cv2.imread('boxes.jpg') 
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) 


# convert to HSV, since red and yellow are the lowest hue colors and come before green
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)

# create a binary thresholded image on hue between red and yellow
#Change these if cube colours changes?
lower =(20, 100, 100)
upper = (30, 255, 255)
#lower = (0,240,160)
#upper = (30,255,255)
thresh = cv2.inRange(hsv, lower, upper)

# apply morphology
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (9,9))
clean = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel)
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (15,15))
clean = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, kernel)

# get external contours
contours = cv2.findContours(clean, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
contours = contours[0] if len(contours) == 2 else contours[1]

result2 = img.copy()

mask = np.zeros(result2.shape, dtype=np.uint8)
thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)[1]
ROI_number = 0

for c in contours:
    cv2.drawContours(result2,[c],0,(0,0,0),2)
    # get rotated rectangle from contour
    rot_rect = cv2.minAreaRect(c)
    box = cv2.boxPoints(rot_rect)
    box = np.int0(box)
    # draw rotated rectangle on copy of img
    cv2.drawContours(result2,[box],0,(0,0,0),2)
    # Gør noget hvis arealet er større end 1.
    # Whats the area of the component?
    areal = cv2.contourArea(c)
    if(areal > 1):
        # get the center of mass
        M = cv2.moments(c)
        cx = int(M['m10']/M['m00'])
        cy = int(M['m01']/M['m00'])
        center = (cx, cy)
        print("nx: ",cx,"ny: ",cy)
        color = (0, 0, 255)

        cv2.circle(result2, center, 3, color, -1)
        cv2.putText(result2, "center", (int(cx) - 10, int(cy) - 20),
        cv2.FONT_HERSHEY_SIMPLEX, 1.2, color, 2)

        x,y,w,h = cv2.boundingRect(c)
        ROI = 255 - thresh[y:y+h, x:x+w]
        cv2.drawContours(mask, [c], -1, (255,255,255), -1)
        cv2.imwrite('ROI_{}.png'.format(ROI_number), ROI)

        #Read saved image (number)
        result = getNumber('ROI_{}.png'.format(ROI_number))
        print("ROI_number: ", result)
        Cubes.append([cx, cy, result])
        ROI_number += 1

# save result
cv2.imwrite("4cubes_result2.png",result2)   

# display result
imS = cv2.resize(result2, (600, 400))
cv2.imshow("result2", imS)
#cv2.imshow('mask', mask)
#cv2.imshow('thresh', thresh)
SortedCubes = sortNumbers(Cubes)
print("nFound array [x, y, Cube_num] = ", Cubes)
print("Sorted array [x, y, Cube_num] = ", SortedCubes)
cv2.waitKey(0)
cv2.destroyAllWindows()

EDIT

I also got these two pictures to try with

4

5

EDIT 2

y:  160
shape:  rectangle 
areal:  723.0
['ta'] : image should have been 4

x:  269 
y:  155
shape:  rectangle 
areal:  637.5
['1'] : image should have been 1

x:  64 
y:  149
shape:  rectangle 
areal:  748.5
['20'] : image should have been 2

x:  125 
y:  141
shape:  rectangle 
areal:  714.5
['5.'] : image should have been 5

x:  222 
y:  127
shape:  rectangle 
areal:  662.5
['3'] : image should have been 3

x:  165 
y:  101
shape:  rectangle 
areal:  666.5
['40'] : image should have been 7

x:  58 
y:  96
shape:  rectangle
areal:  782.5
['L', 'RJ'] : image should have been 8

x:  279
y:  90
shape:  rectangle
areal:  653.5
['EJ'] : image should have been 9

x:  107
y:  84
shape:  rectangle
areal:  717.5
['C'] : image should have been 6

All the images is cut from this image

4 4 1 2 5 3 7 8 9 6

Answer

You may need a different processing method and you may need to set the page-segmentation-mode (psm) for the tesseract

Here, when you apply adaptive-thresholding (at) with bitwise_notoperation

enter image description here

Now, when you read the image with psm mode 6 (Assume a single uniform block of text.)

Result will be:

['34', '215', '28', '7', '5']

Unfortunately 6 and 9 are not detected, maybe if you change the at parameters you can find a better result.

Code:


import cv2
import pytesseract

img = cv2.imread("u3ZTw.png")
gry = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
thr = cv2.adaptiveThreshold(gry, 255, cv2.ADAPTIVE_THRESH_MEAN_C,
                            cv2.THRESH_BINARY_INV, 59, 88)
bnt = cv2.bitwise_not(thr)
txt = pytesseract.image_to_string(bnt, config="--psm 6 digits")
txt = txt.strip().split("n")
print(txt)
cv2.imshow("bnt", bnt)
cv2.waitKey(0)

For photos in the Edit:


You must change the adaptive-threshold parameters for each different image.

To be more specific, you must change the blockSize and C params to get the expected result.

For instance for number 4 and 5, I changed the:

  • blockSize: 31 (previously 59)

  • C: 30 (previously 88)

  • import cv2
    import pytesseract
    
    img = cv2.imread("num5.png")
    gry = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    thr = cv2.adaptiveThreshold(gry, 255, cv2.ADAPTIVE_THRESH_MEAN_C,
                                 cv2.THRESH_BINARY_INV, 31, 30)
    bnt = cv2.bitwise_not(thr)
    txt = pytesseract.image_to_string(bnt, config="--psm 6 digits")
    txt = txt.strip().split("n")
    print(txt)
    cv2.imshow("bnt", bnt)
    cv2.waitKey(0)
    
  • Images:

    • enter image description here enter image description here
  • Result:

    • ['4']
      
    • ['5']