how to extract numbers from captcha image in python? - python

I want to extract numbers from captcha image, so I tried this code from this answer this answer:
try:
from PIL import Image
except ImportError:
import Image
import pytesseract
import cv2
file = 'sample.jpg'
img = cv2.imread(file, cv2.IMREAD_GRAYSCALE)
img = cv2.resize(img, None, fx=10, fy=10, interpolation=cv2.INTER_LINEAR)
img = cv2.medianBlur(img, 9)
th, img = cv2.threshold(img, 185, 255, cv2.THRESH_BINARY)
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (4,8))
img = cv2.morphologyEx(img, cv2.MORPH_CLOSE, kernel)
cv2.imwrite("sample2.jpg", img)
file = 'sample2.jpg'
text = pytesseract.image_to_string(file)
print(''.join(x for x in text if x.isdigit()))
and it worked fine for this image: outPut: 436359 But, when I tried it on this image: It gave me nothing, outPut: .
How can I modify my code to get the numbers as a string from the second image?
EDIT:
I tried Matt's answer and it worked just fine for the image above. but it doesn't recognise numbers like (8,1) in image A, and number (7) in image B
image A
image B
How to fix that?

Often, getting OCR just right on an image like this has to do with the order and parameters of the transformations. For example, in the following code snippet, I first convert to grayscale, then erode the pixels, then dilate, then erode again. I use threshold to convert to binary (just blacks and whites) and then dilate and erode one more time. This for me produces the correct value of 859917 and should be reproducible.
import cv2
import numpy as np
import pytesseract
file = 'sample2.jpg'
img = cv2.imread(file)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ekernel = np.ones((1,2),np.uint8)
eroded = cv2.erode(gray, ekernel, iterations = 1)
dkernel = np.ones((2,3),np.uint8)
dilated_once = cv2.dilate(eroded, dkernel, iterations = 1)
ekernel = np.ones((2,2),np.uint8)
dilated_twice = cv2.erode(dilated_once, ekernel, iterations = 1)
th, threshed = cv2.threshold(dilated_twice, 200, 255, cv2.THRESH_BINARY)
dkernel = np.ones((2,2),np.uint8)
threshed_dilated = cv2.dilate(threshed, dkernel, iterations = 1)
ekernel = np.ones((2,2),np.uint8)
threshed_eroded = cv2.erode(threshed_dilated, ekernel, iterations = 1)
text = pytesseract.image_to_string(threshed_eroded)
print(''.join(x for x in text if x.isdigit()))

Related

Can't get numbers from image with python, Tesseract and opencv

i have to get numbers from a water-meter image usign python tesseract and opencv.
I have tried to change the --psm but it's doesn't work.
Here the image without modification :
enter image description here
Here the outpout image :
enter image description here
I need your help guys, i'm starting python and i'm already blocked :'(
My code :
from PIL import Image
import pytesseract
import cv2
import numpy as np
import urllib
import requests
pytesseract.pytesseract.tesseract_cmd = r'C:\Users\Hymed\AppData\Local\Tesseract-OCR\tesseract.exe'
col = Image.open("pts.jpg")
gray = col.convert('L')
bw = gray.point(lambda x: 0 if x<128 else 255, '1')
bw.save("cp19.png")
image = cv2.imread('cp19.png')
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
thresh = 255 - cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)[1]
# Blur and perform text extraction
thresh = cv2.GaussianBlur(thresh, (3,3), 0)
img1 = np.array(thresh)
data = pytesseract.image_to_string(img1, config='--psm 11 digits')
print(data)
cv2.imshow('thresh', thresh)
cv2.waitKey()
You have nearly finished the task.
I use the divide operation, after the GaussianBlur.
div = cv2.divide(gray, thresh, scale=192)
Result:
When I read from the image:
data = pytesseract.image_to_string(div, config='--psm 11 digits')
print(data)
Result:
00000161
Code: (Just added div = cv2.divide(gray, thresh, scale=192) rest are your code)
from PIL import Image
import pytesseract
import cv2
import numpy as np
col = Image.open("TOaEW.jpg")
gray = col.convert('L')
bw = gray.point(lambda x: 0 if x < 128 else 255, '1')
bw.save("cp19.png")
image = cv2.imread('cp19.png')
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
thresh = 255 - cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)[1]
# Blur and perform text extraction
thresh = cv2.GaussianBlur(thresh, (3, 3), 0)
div = cv2.divide(gray, thresh, scale=192) # added
data = pytesseract.image_to_string(div, config='--psm 11 digits')
print(data)
I tried to read the number from an image using Tesseract. Except the numbers shown in the first line, it also returned an unidentified symbol in the second line. I don't understand what I did wrong. Here is the code and the results
code and output
This is the image I extracted the number from:
Image used for number extraction

Pytesseract - OCR on image with text in different colors

Pytesseract is unable to extract text when texts are present in different colors . I tried using opencv to invert the image but it doesn't work for dark text colors.
The image:
import cv2
import pytesseract
from PIL import Image
def text(image):
image = cv2.resize(image, (0, 0), fx=7, fy=7)
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
cv2.imwrite("gray.png", gray)
blur = cv2.GaussianBlur(gray, (3, 3), 0)
cv2.imwrite("gray_blur.png", blur)
thresh = cv2.threshold(blur, 127, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)[1]
cv2.imwrite("thresh.png", thresh)
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3, 3))
opening = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel, iterations=1)
cv2.imwrite("opening.png", opening)
invert = 255 - opening
cv2.imwrite("invert.png", invert)
data = pytesseract.image_to_string(invert, lang="eng", config="--psm 7")
return data
Is there a way to extract both the texts from the given image: DEADLINE(red) and WHITE HOUSE(white)
You can use ImageOps to invert the image.And binaryzate the Image.
import pytesseract
from PIL import Image,ImageOps
import numpy as np
img = Image.open("OCR.png").convert("L")
img = ImageOps.invert(img)
# img.show()
threshold = 240
table = []
pixelArray = img.load()
for y in range(img.size[1]): # binaryzate it
List = []
for x in range(img.size[0]):
if pixelArray[x,y] < threshold:
List.append(0)
else:
List.append(255)
table.append(List)
img = Image.fromarray(np.array(table)) # load the image from array.
# img.show()
print(pytesseract.image_to_string(img))
The result:
The img in the end like this:

Middle line between 2 contour lines in opencv python?

I have an image
After my code runs,
The new image is
I need to find the line between them like this
How do I do?
My code
import numpy as np
import cv2
import cv2 as cv
ima = cv2.imread('track1.pNg')
imgray = cv2.cvtColor(ima,cv2.COLOR_BGR2GRAY)
im = cv2.cvtColor(ima,cv2.COLOR_BGR2GRAY)
imm = cv2.inRange(im,(0),(49))
kernel = np.ones((5,5),np.uint8)
gradient = cv2.morphologyEx(imm, cv2.MORPH_GRADIENT, kernel)
il = cv2.dilate(gradient, kernel, iterations=7)
ol = cv2.erode(il, kernel, iterations=7)
contours,hei = cv2.findContours(ol,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)
img = cv2.drawContours(ima, contours, -1, (200,255,0), 3)
cv2.imshow('window',ima)
How can i achieve this?
This answer explains how to find a line that runs between two sides of a shape. The center can be found by iteratively eroding the image.
This is the result:
This is the code I used:
import cv2
import numpy as np
img = 255-cv2.imread('/home/stephen/Desktop/PITqe.png',0)
kernel = np.ones((20,20), np.uint8)
img = cv2.erode(img, kernel, iterations=2)
size = np.size(img)
skel = np.zeros(img.shape,np.uint8)
ret,img = cv2.threshold(img,127,255,0)
element = cv2.getStructuringElement(cv2.MORPH_CROSS,(3,3))
done = False
while( not done):
eroded = cv2.erode(img,element)
temp = cv2.dilate(eroded,element)
temp = cv2.subtract(img,temp)
skel = cv2.bitwise_or(skel,temp)
img = eroded.copy()
zeros = size - cv2.countNonZero(img)
cv2.imshow('img', img)
cv2.waitKey(100)
if zeros==size:
done = True
cv2.imshow("img",skel)
cv2.waitKey(0)
cv2.destroyAllWindows()
Here is another way to skeletonize in OpenCV (without explicitly iterating) by using distance transform and top hat morphology.
Input:
import cv2
import numpy as np
# read image and invert so blob is white on black background
img = 255-cv2.imread('tall_blob.png',0)
# do some eroding of img, but not too much
kernel = np.ones((20,20), np.uint8)
img = cv2.erode(img, kernel, iterations=2)
# threshold img
ret, thresh = cv2.threshold(img,127,255,0)
# do distance transform
dist = cv2.distanceTransform(thresh, distanceType=cv2.DIST_L2, maskSize=5)
# set up cross for tophat skeletonization
kernel = cv2.getStructuringElement(cv2.MORPH_CROSS,(3,3))
skeleton = cv2.morphologyEx(dist, cv2.MORPH_TOPHAT, kernel)
# threshold skeleton
ret, skeleton = cv2.threshold(skeleton,0,255,0)
# display skeleton
cv2.imshow("skeleton",skeleton)
cv2.waitKey(0)
cv2.destroyAllWindows()
# save results
cv2.imwrite('tall_blob_skeleton.png', skeleton)

How to improve text extraction from an image?

I am using pytesseract to extract text from images. Before extracting text with pytesseract, I use Pillow and cv2 to reduce noise and enhance the image:
import numpy as np
import pytesseract
from PIL import Image, ImageFilter, ImageEnhance
import cv2
img = cv2.imread('ss.png')
img = cv2.resize(img, (0,0), fx=3, fy=3)
cv2.imwrite("new.png", img)
img1 = cv2.imread("new.png", 0)
#Apply dilation and erosion
kernel = np.ones((2, 2), np.uint8)
img1 = cv2.dilate(img1, kernel, iterations=1)
img1 = cv2.erode(img1, kernel, iterations=1)
img1 = cv2.adaptiveThreshold(img1,255,cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY_INV,11,2)
cv2.imwrite("new1.png", img1)
img2 = Image.open("new1.png")
#Enhance the image
img2 = im.filter(ImageFilter.MedianFilter())
enhancer = ImageEnhance.Contrast(im)
img2 = enhancer.enhance(2)
img2.save('new2.png')
result = pytesseract.image_to_string(Image.open("new2.png"))
print(result)
I mostly get good results, but when I use some low quality/resolution images, I do not get the expected output. Can I improve this in my code?
Example:
Input:
new1.png:
new2.png:
The string that I get from the console is play. What could I change in my algorithm, so that I get the whole string extracted?
Any help would be greatly appreciated.
This is a late answer, but I just came across this. we can use Pillow and cv2 to reduce noise and enhance the image before extracting text from images using pytesseract. I hope it would help someone in future.
#import required library
src_path = "C:/Users/chethan/Desktop/"
def get_string(img_path):
# Read image with opencv
img = cv2.imread(img_path)
# Convert to gray
img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# Apply dilation and erosion to remove some noise
kernel = np.ones((1, 1), np.uint8)
img = cv2.dilate(img, kernel, iterations=1)
img = cv2.erode(img, kernel, iterations=1)
# Write image after removed noise
cv2.imwrite(src_path + "removed_noise.png", img)
# Apply threshold to get image with only black and white
#img = cv2.adaptiveThreshold(img, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 31, 2)
# Write the image after apply opencv to do some ...
cv2.imwrite(src_path + "thres.png", img)
# Recognize text with tesseract for python
result = pytesseract.image_to_string(Image.open(src_path + "thres.png"))
# Recognize text with tesseract for python
result = pytesseract.image_to_string(Image.open(img_path))
# Remove template file
# os.remove(temp)
return result
print(get_string(src_path + "dummy.png"))

expected string or Unicode object, Photo found in django

here i want to read the image from db and apply some operations on my image like noise remove .... and finally i will appy pytesseract to get the text
def GetData(request):
img = Photo.objects.get(id=1)
#wrapper = FileWrapper(open(img.file))
# Read image with opencv
img = cv2.imread(img)
# Apply dilation and erosion to remove some noise
kernel = np.ones((1, 1), np.uint8)
img = cv2.dilate(img, kernel, iterations=1)
img = cv2.erode(img, kernel, iterations=1)
b,g,r = cv2.split(img)
# get b,g,r
rgb_img = cv2.merge([r,g,b])
# switch it to rgb
# Denoising
dst = cv2.fastNlMeansDenoisingColored(img,None,10,10,7,21)
img = cv2.cvtColor(dst, cv2.COLOR_BGR2GRAY)
# Apply threshold to get image with only black and white
img = cv2.adaptiveThreshold(img, 127, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 11,2)
new_image = cv2.blur(img, (1, 1))
The error comes from cv2.imread(img) because imread take a string or unicode parameter with the URI of the image, but you are using a Django model class which is quite different.
Assuming that your Photo class model has an ImageField field named image you could fix your issue changing
img = cv2.imread(img)
to something like
img = cv2.imread(img.image.url)

Categories