Related
I'm currently working on a small OCR bot. I got pretty much everything to work and am now trying to improve the OCR. Specifically, it has problems with two things: the orange/red-ish text on the same colored gradient and for some reason the first 1 of "1/1". Sadly I haven't found anything that worked in my case yet. I've made a small test image, which is consisting of multiple images, below:
Source Image
Results
Adaptive Threshold
As you can see the gradient results in a blob that is sometimes big enough to overlap with the first word (see "apprentice") resulting in garbage.
I've tried many variations and played around with thresholds, blurs, erode, dilation, box detection with the dilation method, etc. but nothing worked well. The only way I did get rid of the blob is using an adaptive Threshold. But sadly I wasn't able to get good results using the output image.
If anyone knows how to make the OCR more robust, increase accuracy and get rid of the blob I'd appreciate your help. Thanks.
The following code is my 'playground' to figure out a better way:
import cv2
import pytesseract
import numpy as np
pytesseract.pytesseract.tesseract_cmd = YOUR_PATH
def resize(img, scale_percent=300):
# use this instead?
# resize = image = imutils.resize(image, width=300)
# automatically resizes it about 300% by default
width = int(img.shape[1] * scale_percent / 100)
height = int(img.shape[0] * scale_percent / 100)
dim = (width, height)
resized = cv2.resize(img, dim, interpolation=cv2.INTER_AREA)
return resized
def preprocessImage(img, scale=300, threshhold=127):
""" input RGB colour space """
# makes results more accurate - inspired from https://stackoverflow.com/questions/58103337/how-to-ocr-image-with-tesseract
# another resource to improve accuracy - https://tesseract-ocr.github.io/tessdoc/ImproveQuality.html
# converts from rgb to grayscale then enlarges it
# applies gaussian blur
# convert to b&w
# invert black and white colours (white background, black text)
grayscale = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
cv2.imshow('grayscale', grayscale)
resized = resize(grayscale, scale)
cv2.imshow('resized', resized)
blurred = cv2.medianBlur(resized, 5)
#cv2.imshow('median', blurred)
blurred = cv2.GaussianBlur(resized, (5, 5), 5)
cv2.imshow('1', blurred)
cv2.waitKey()
blackAndWhite = cv2.threshold(blurred, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)[1]
cv2.imshow('blackAndWhite', blackAndWhite)
th3 = cv2.adaptiveThreshold(blurred, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY_INV, 11, 2)
cv2.floodFill(th3, None, (0, 0), 255)
cv2.imshow('th3', th3)
#kernel = np.ones((3, 3), np.uint8)
#erode = cv2.erode(th3, kernel)
kernel = np.ones((5, 5), np.uint8)
#opening = cv2.morphologyEx(blackAndWhite, cv2.MORPH_OPEN, kernel)
invertedColours = cv2.bitwise_not(blackAndWhite)
return invertedColours
# excerpt from https://www.youtube.com/watch?v=6DjFscX4I_c
def imageToText(img):
# returns item name from image, preprocess if needed
boxes = pytesseract.image_to_data(img)
num = []
for count, box in enumerate(boxes.splitlines()):
if (count != 0):
box = box.split()
if (len(box) == 12):
text = box[11].strip('#®')
if (text != ''):
num.append(text)
text = ' '.join(num)
## Alternate method
# text = pytesseract.image_to_string(img)
# print("Name:", text)
return text
if __name__ == "__main__":
img = cv2.imread("test.png")
img = preprocessImage(img, scale=300)
print(imageToText(img))
##############################################
##### Detecting Words ######
##############################################
#[ 0 1 2 3 4 5 6 7 8 9 10 11 ]
#['level', 'page_num', 'block_num', 'par_num', 'line_num', 'word_num', 'left', 'top', 'width', 'height', 'conf', 'text']
boxes = pytesseract.image_to_data(img)
# convert back to colored image
img = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR)
# draw boxes and text
for a,b in enumerate(boxes.splitlines()):
print(b)
if a!=0:
b = b.split()
if len(b)==12:
x,y,w,h = int(b[6]),int(b[7]),int(b[8]),int(b[9])
cv2.putText(img,b[11],(x,y-5),cv2.FONT_HERSHEY_SIMPLEX,1,(50,50,255),2)
cv2.rectangle(img, (x,y), (x+w, y+h), (0, 0, 255), 2)
cv2.imshow('img', img)
cv2.waitKey(0)
I couldn't get it perfect but almost...
I got a lot of benefit from CLAHE equalization. See tutorial here. But that wasn't enough. Still needed thresholding. Adaptive techniques didn't work well, but cv2.THRESH_TOZERO gives OK results. See thresholding tutorial here
import cv2
from pytesseract import image_to_string, image_to_data
img = cv2.imread('gradient.png', cv2.IMREAD_GRAYSCALE)
img = cv2.resize(img, (0,0), fx=2.0, fy=2.0)
clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8,8))
img = clahe.apply(img)
img = 255-img # invert image. tesseract prefers black text on white background
ret, img = cv2.threshold(img, 127, 255, cv2.THRESH_TOZERO)
cv2.imwrite('output.png', img)
ocr = image_to_string(img, config='--psm 6')
print(ocr)
which gives ocr output
Tool Crafting Part
Apprentice Craft Kit
Adept Craft Kit
Expert Craft Kit
=
Master Craft Kit
1/1
So I've ben trying to detect a number (1-9) inside a yellow cube, but without a solid solution..
This is two of my pictures
This is one solution I've been trying, but without any luck
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 Files\Tesseract-OCR\tesseract'
img = cv2.imread("ROI_0.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)
Is there another way to do this, because it's not working?
Steps:
Binarize(otsu's method)
Correct skew using minAreaRect
Find max area contour
crop the number
pass cropped to pytesseract
image = cv2.imread("y6.png")
# image = image_resize(image,width=480,height=640)
gray = cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)
thresh = cv2.threshold(gray,0,255,cv2.THRESH_BINARY+cv2.THRESH_OTSU)[1]
contours = cv2.findContours(thresh,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)[0]
big = max(contours,key=cv2.contourArea)
(x,y),(w,h),angle = cv2.minAreaRect(big)
print(angle)
(h, w) = image.shape[:2]
center = (w // 2, h // 2)
M = cv2.getRotationMatrix2D(center, angle, 1.0)
rotated = cv2.warpAffine(thresh, M, (w, h),flags=cv2.INTER_CUBIC, borderMode=cv2.BORDER_CONSTANT,borderValue=(0,0,0))
big = cv2.findContours(rotated,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)[0]
big = max(big,key=cv2.contourArea)
x,y,w,h = cv2.boundingRect(big)
# cropped = rotated[y:y+h,x:x+w]
cropped = rotated[y:y+h-h//10,w//6:x+w-w//6]
data = pytesseract.image_to_string(cropped,config='--psm 6 digits')# -c tessedit_char_whitelist = 0123456789')
print(data)
There are a few hardcoded values like h//10 and all in cropping. So optimization is needed.
You need to remove the black border first for tesseract to work. Just replace the black background with white color and then apply thresholding such that it can remove both white border and yellow color at the same time and then use tesseract to detect the character.
I'm trying to detect this Code128 barcode with Python + zbar module:
(Image download link here).
This works:
import cv2, numpy
import zbar
from PIL import Image
import matplotlib.pyplot as plt
scanner = zbar.ImageScanner()
pil = Image.open("000.jpg").convert('L')
width, height = pil.size
plt.imshow(pil); plt.show()
image = zbar.Image(width, height, 'Y800', pil.tobytes())
result = scanner.scan(image)
for symbol in image:
print symbol.data, symbol.type, symbol.quality, symbol.location, symbol.count, symbol.orientation
but only one point is detected: (596, 210).
If I apply a black and white thresholding:
pil = Image.open("000.jpg").convert('L')
pil = pil .point(lambda x: 0 if x<100 else 255, '1').convert('L')
it's better, and we have 3 points: (596, 210), (482, 211), (596, 212). But it adds one more difficulty (finding the optimal threshold - here 100 - automatically for every new image).
Still, we don't have the 4 corners of the barcode.
Question: how to reliably find the 4 corners of a barcode on an image, with Python? (and maybe OpenCV, or another library?)
Notes:
It is possible, this is a great example (but sadly not open-source as mentioned in the comments):
Object detection, very fast and robust blurry 1D barcode detection for real-time applications
The corners detection seems to be excellent and very fast, even if the barcode is only a small part of the whole image (this is important for me).
Interesting solution: Real-time barcode detection in video with Python and OpenCV but there are limitations of the method (see in the article: the barcode should be close up, etc.) that limit the potential use. Also I'm more looking for a ready-to-use library for this.
Interesting solution 2: Detecting Barcodes in Images with Python and OpenCV but again, it does not seem like a production-ready solution, but more a research in progress. Indeed, I tried their code on this image but the detection does not yield successful result. It has to be noted that it doesn't take any spec of the barcode in consideration for the detection (the fact there's a start/stop symbol, etc.)
import numpy as np
import cv2
image = cv2.imread("000.jpg")
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
gradX = cv2.Sobel(gray, ddepth = cv2.CV_32F, dx = 1, dy = 0, ksize = -1)
gradY = cv2.Sobel(gray, ddepth = cv2.CV_32F, dx = 0, dy = 1, ksize = -1)
gradient = cv2.subtract(gradX, gradY)
gradient = cv2.convertScaleAbs(gradient)
blurred = cv2.blur(gradient, (9, 9))
(_, thresh) = cv2.threshold(blurred, 225, 255, cv2.THRESH_BINARY)
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (21, 7))
closed = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, kernel)
closed = cv2.erode(closed, None, iterations = 4)
closed = cv2.dilate(closed, None, iterations = 4)
(_, cnts, _) = cv2.findContours(closed.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
c = sorted(cnts, key = cv2.contourArea, reverse = True)[0]
rect = cv2.minAreaRect(c)
box = np.int0(cv2.boxPoints(rect))
cv2.drawContours(image, [box], -1, (0, 255, 0), 3)
cv2.imshow("Image", image)
cv2.waitKey(0)
Solution 2 is pretty good. The critical factor that made it fail on your image was the thresholding. If you drop the parameter 225 way down to 55, you'll get much better results.
I've reworked the code, making some tweaks here and there. The original code is fine if you prefer. The documentation for OpenCV is quite good, and there are very good Python tutorials.
import numpy as np
import cv2
image = cv2.imread("barcode.jpg")
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# equalize lighting
clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8,8))
gray = clahe.apply(gray)
# edge enhancement
edge_enh = cv2.Laplacian(gray, ddepth = cv2.CV_8U,
ksize = 3, scale = 1, delta = 0)
cv2.imshow("Edges", edge_enh)
cv2.waitKey(0)
retval = cv2.imwrite("edge_enh.jpg", edge_enh)
# bilateral blur, which keeps edges
blurred = cv2.bilateralFilter(edge_enh, 13, 50, 50)
# use simple thresholding. adaptive thresholding might be more robust
(_, thresh) = cv2.threshold(blurred, 55, 255, cv2.THRESH_BINARY)
cv2.imshow("Thresholded", thresh)
cv2.waitKey(0)
retval = cv2.imwrite("thresh.jpg", thresh)
# do some morphology to isolate just the barcode blob
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (9, 9))
closed = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, kernel)
closed = cv2.erode(closed, None, iterations = 4)
closed = cv2.dilate(closed, None, iterations = 4)
cv2.imshow("After morphology", closed)
cv2.waitKey(0)
retval = cv2.imwrite("closed.jpg", closed)
# find contours left in the image
(_, cnts, _) = cv2.findContours(closed.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
c = sorted(cnts, key = cv2.contourArea, reverse = True)[0]
rect = cv2.minAreaRect(c)
box = np.int0(cv2.boxPoints(rect))
cv2.drawContours(image, [box], -1, (0, 255, 0), 3)
print(box)
cv2.imshow("found barcode", image)
cv2.waitKey(0)
retval = cv2.imwrite("found.jpg", image)
edge.jpg
thresh.jpg
closed.jpg
found.jpg
output from console:
[[596 249]
[470 213]
[482 172]
[608 209]]
For the following to work, you need to have contrib package installed using pip install opencv-contrib-python
Your OpenCV version would now have a separate class for detecting barcodes.
cv2.barcode_BarcodeDetector() comes equipped with 3 in-built functions:
decode(): returns decoded information and type
detect(): returns the 4 corner points enclosing each detected barcode
detectAndDecode(): returns all the above
Sample Image used is from pyimagesearch blog:
The 4 corners are captured in points.
Code:
img = cv2.imread('barcode.jpg')
barcode_detector = cv2.barcode_BarcodeDetector()
# 'retval' is boolean mentioning whether barcode has been detected or not
retval, decoded_info, decoded_type, points = barcode_detector.detectAndDecode(img)
# copy of original image
img2 = img.copy()
# proceed further only if at least one barcode is detected:
if retval:
points = points.astype(np.int)
for i, point in enumerate(points):
img2 = cv2.drawContours(img2,[point],0,(0, 255, 0),2)
# uncomment the following to print decoded information
#x1, y1 = point[1]
#y1 = y1 - 10
#cv2.putText(img2, decoded_info[i], (x1, y1), cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 255, 0), 3, 2)
Result:
Detected barcode:
Detected barcode and information:
One solution not discussed here is PyZbar.
It is helpful to know there are a number of different types of barcode so reading this can be helpful. Now each solution for decoding might have limitations for the types it can decode. #Jeru Luke's solution seems to be only support EAN-13 barcodes currently see docs here.
Now using PyZbar a simple solution for getting the rect object (4 corners) with the decoding and the bonus of finding out which type the barcode it is can be done with this script.
Using this barcode
import cv2
from pyzbar.pyzbar import decode
file_path = r'c:\my_file'
img = cv2.imread(file_path)
detectedBarcodes = decode(img)
for barcode in detectedBarcodes:
(x, y, w, h) = barcode.rect
cv2.rectangle(img, (x, y), (x + w, y + h), (255, 0, 0), 5)
print(barcode.rect)
print(barcode.data)
print(barcode.type)
output
Rect(left=77, top=1, width=665, height=516)
b'9771234567003'
EAN13
Using #Jeru Luke's code you can drawContours and putText.
ZBar supports
EAN-13/UPC-A,
UPC-E, EAN-8,
Code 128,
Code 93,
Code 39,
Codabar,
Interleaved 2 of 5,
QR Code
SQ Code.
So I think PyZbar will also support these types.
I am trying to take the below image, trace the white shape, and export the resulting path to pdf. The problem I have is that findContours seeming only finds points along the edge of the shape. Is there a solution out there, similar to findContours, that detects curves in a shape and replaces its points with a spline wherever there is a curve? If I use scipy.interpolate it ignores straight lines and turns the entire contour into one big curved shape, which is no good either. I need something that does both things.
import numpy as np
import cv2
from scipy.interpolate import splprep, splev
from pyx import *
import matplotlib.pyplot as plt
#read in image file
original = cv2.imread('test.jpg')
#blur the image to smooth edges
im = cv2.medianBlur(original,5)
#threshold the image
imgray = cv2.cvtColor(im,cv2.COLOR_BGR2GRAY)
ret,thresh = cv2.threshold(imgray,170,255,cv2.THRESH_BINARY)
#findContours
im2, contours, hierarchy = cv2.findContours(thresh,cv2.RETR_EXTERNAL,cv2.CHAIN_\
APPROX_SIMPLE)
#drawContours
cv2.drawContours(original, [approx], -1, (0,255,0), 3)
cv2.imshow("Imageee", original)
cv2.waitKey(0)
Except using cv2.findContours with flag cv2.CHAIN_APPROX_SIMPLE to approx the contours, we can do it manually.
use cv2.findContours with flag cv2.CHAIN_APPROX_NONE to find contours.
use cv2.arcLength to calculate the contour length.
use cv2.approxPoolyDP to approx the contour manually with epsilon = eps * arclen.
Here is one of the results when eps=0.005:
More results:
#!/usr/bin/python3
# 2018.01.04 13:01:24 CST
# 2018.01.04 14:42:58 CST
import cv2
import numpy as np
import os
img = cv2.imread("test.jpg")
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ret,threshed = cv2.threshold(gray,170,255,cv2.THRESH_BINARY)
# find contours without approx
cnts = cv2.findContours(threshed,cv2.RETR_LIST,cv2.CHAIN_APPROX_NONE)[-2]
# get the max-area contour
cnt = sorted(cnts, key=cv2.contourArea)[-1]
# calc arclentgh
arclen = cv2.arcLength(cnt, True)
# do approx
eps = 0.0005
epsilon = arclen * eps
approx = cv2.approxPolyDP(cnt, epsilon, True)
# draw the result
canvas = img.copy()
for pt in approx:
cv2.circle(canvas, (pt[0][0], pt[0][1]), 7, (0,255,0), -1)
cv2.drawContours(canvas, [approx], -1, (0,0,255), 2, cv2.LINE_AA)
# save
cv2.imwrite("result.png", canvas)
I think your problem actually consists of two issues.
The first issue is to extract the contour, which you can achieve using teh findContour function:
import numpy as np
print cv2.__version__
rMaskgray = cv2.imread('test.jpg', 0)
(thresh, binRed) = cv2.threshold(rMaskgray, 200, 255, cv2.THRESH_BINARY)
_, Rcontours, hier_r = cv2.findContours(binRed,cv2.RETR_CCOMP,cv2.CHAIN_APPROX_SIMPLE)
r_areas = [cv2.contourArea(c) for c in Rcontours]
max_rarea = np.argmax(r_areas)
CntExternalMask = np.ones(binRed.shape[:2], dtype="uint8") * 255
contour= Rcontours[max_rarea]
cv2.drawContours(CntExternalMask,[contour],-1,0,1)
print "These are the contour points:"
print c
print
print "shape: ", c.shape
for p in contour:
print p[0][0]
cv2.circle(CntExternalMask, (p[0][0], p[0][1]), 5, (0,255,0), -1)
cv2.imwrite("contour.jpg", CntExternalMask)
cv2.imshow("Contour image", CntExternalMask)
cv2.waitKey(0)
If you execute the program, the contour points are printed as a list of point coordinates.
The contour approximation method you choose influences the interpolation which is actually used (and the number of points found), as described here. I have added small dots at the points found with the approximation method cv2.CHAIN_APPROX_SIMPLE. You see that the straight lines are already approximated.
I may not fully have understood your second step, though. You want to omit some of those points, replacing point lists partially by splines. There might be different way to do this, depending on your final intention. Do you just want to replace the straight lines? If you replace curved parts, what is the margin of error you are allowing?
# import the necessary packages
import numpy as np
import argparse
import glob
import cv2
#For saving pdf
def save_pdf(imagename):
import img2pdf
# opening from filename
with open("output.pdf","wb") as f:
f.write(img2pdf.convert(imagename))
#for fouind biggest contours
def bigercnt(contours):
max_area=0
cnt=[]
for ii in contours:
area=cv2.contourArea(ii)
if area>max_area:
cnt = ii
return cnt
#STARTING
print ("Reading img.jpg file")
# load the image, convert it to grayscale, and blur it slightly
image = cv2.imread('img.jpg')
image = cv2.resize(image, (0,0), fx=0.5, fy=0.5)
print ("Converting it gray scale")
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
print ("Bluring")
blurred = cv2.GaussianBlur(gray, (3, 3), 0)
print ("Looking for edges" )
# apply Canny edge detection using a wide threshold, tight
# threshold, and automatically determined threshold
tight = cv2.Canny(blurred, 255, 250)
print ("Looking for contours")
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (10, 10))
close = cv2.morphologyEx(tight, cv2.MORPH_CLOSE, kernel)
_,contours, hierarchy = cv2.findContours( close.copy(), cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
print("Looking for big contour")
cnt = bigercnt(contours)
print ("Cropping found contour")
x,y,w,h = cv2.boundingRect(cnt)
croped_image = image[y:y+h,x:x+w]
img2 = np.zeros((h,w,4),np.uint8)
print ("Taking only pixels in countour and creating png")
for i in range(h):
for j in range(w):
#print (x+j, y+i)
#print cv2.pointPolygonTest(cnt, (x+j, y+i), False)
if cv2.pointPolygonTest(cnt, (x+j, y+i), False)==1:
#print True
img2[i,j] = [croped_image[i, j][0],croped_image[i, j][1],croped_image[i, j][2],255]
else:
img2[i,j] = [255,255,255,0]
print ("Showing output image")
# Show the output image
#cv2.imshow('croped', croped_image)
cv2.imshow('output', img2)
params = list()
params.append(cv2.IMWRITE_PNG_COMPRESSION)
params.append(8)
print ("Saving output image")
cv2.imwrite("output.png",img2,params)
print ("Finish:converted")
cv2.waitKey(0)
cv2.destroyAllWindows()
I have what may be a simple issue in some ~55 lines of code for Python 2.7 OCR for handwritten digits. I obtained this code from a blog and am using it for hobby purposes. I am utilizing cv2, sklearn, skimage, and numpy to assist in the digit recognition.
I have a simple question for the code here - at the end of the for loop I append the number that is "recognized" from sklearn to a numpy array. This works fine, however, the numbers are all out of order. For example, if an image I upload has handwritten "9 8 7 5 4 3" it will print as [5, 4, 3, 9, 7, 8]
I've been staring at this for a while and I can't seem to figure out why it is looping "out of order." I don't know if that is how OpenCV is detecting the numbers, or if it is a function of sklearn - or just a simple logic issue.
Here is the code (the issue I'm having is at the very end - appending to array):
# Import the modules
import cv2
from sklearn.externals import joblib
from skimage.feature import hog
import numpy as np
# Load the classifier
clf = joblib.load("digits_cls.pkl")
# Read the input image
im = cv2.imread("4.jpg")
# Convert to grayscale and apply Gaussian filtering
im_gray = cv2.cvtColor(im, cv2.COLOR_BGR2GRAY)
im_gray = cv2.GaussianBlur(im_gray, (5, 5), 0)
# Threshold the image
ret, im_th = cv2.threshold(im_gray, 90, 255, cv2.THRESH_BINARY_INV)
cv2.imshow("Threshhold/gray", im_th)
# Find contours in the image
hier, ctrs, hier = cv2.findContours(im_th.copy(), cv2.RETR_EXTERNAL,
cv2.CHAIN_APPROX_SIMPLE)
# Get rectangles contains each contour
rects = [cv2.boundingRect(ctr) for ctr in ctrs]
# For each rectangular region, calculate HOG features and predict
# the digit using Linear SVM.
numlist = []
for rect in rects:
# Draw the rectangles
cv2.rectangle(im, (rect[0], rect[1]), (rect[0] + rect[2], rect[1] +
rect[3]), (0, 255, 0), 3)
# Make the rectangular region around the digit
leng = int(rect[3] * 1.6)
pt1 = int(rect[1] + rect[3] // 2 - leng // 2)
pt2 = int(rect[0] + rect[2] // 2 - leng // 2)
roi = im_th[pt1:pt1+leng, pt2:pt2+leng]
# Resize the image
roi = cv2.resize(roi, (28, 28), interpolation=cv2.INTER_AREA)
roi = cv2.dilate(roi, (3, 3))
# Calculate the HOG features
roi_hog_fd = hog(roi, orientations=9, pixels_per_cell=(14, 14), cells_per_block=(1, 1), visualise=False)
nbr = clf.predict(np.array([roi_hog_fd], 'float64'))
cv2.putText(im, str(int(nbr[0])), (rect[0], rect[1]),cv2.FONT_HERSHEY_DUPLEX, 2, (0, 255, 255), 3)
# Appending output to array for further processing
number = (int(nbr[0]))
numlist.append(number)
print numlist
cv2.imshow("Resulting Image with Rectangular ROIs", im)
#cv2.destroyAllWindows()
cv2.waitKey()
You should sort rects by x value before ocr.
rects = sorted(rects, key = lambda rect: rect[0] + rect[2]//2)