├── Data.zip ├── README.md ├── images ├── .gitkeep ├── 2018.png ├── ger1.png ├── ger2.png ├── ger3.png ├── ger4.png ├── pro1.png ├── pro2.png └── pro3.png ├── picture_from_webcam.py ├── read_from_disk.py └── video_from_webam.py /Data.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DevashishPrasad/Embossed-Text-Reader/87888c1f2a74ac72c33b3895f1f15b30293e0067/Data.zip -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Embossed-Text-Reader 2 | This is a tesseract based OCR to read Embossed text on metals. It can also be use as a general OCR. 3 | 4 | This is not generalized solution. The satements and parameters of some funtions will changed according to the texture, 5 | color, lighting effect and visibility of text of image. 6 | 7 | Tesseract is an open-sourced OCR which is capable of reading text from papers, pdfs and other clean formats. Tesseract fails when 8 | tried to perform OCR on noisy and dirty images (for eg. Embossed or Engraved text). The code uses opencv image filtering techniques 9 | to filter the images as clean as possible and then feeds it to Tesseract. 10 | 11 | ## Dependencies 12 | Python
13 | OpenCV
14 | Tesseract
15 | Numpy
16 | imutils
17 | 18 | ### There are three files - 19 | 1. picture_from_webcam.py clicks a single picture from webcam and performs OCR on it 20 | 2. read_from_disk.py loads a picture from an image file 21 | 3. video_from_webcam.py feeds the realtime video from the webcam (doesn't perform as expected) 22 | 23 | ## Example 24 | #### Edged image (canny) with dialation 25 | Picture not availble in your browser 26 | 27 | #### Finding and deleting unessesary contours 28 | 29 | Picture not availble in your browser 30 | 31 | #### Inverted threshold image 32 | 33 | Picture not availble in your browser 34 | 35 | #### Results 36 | 37 | Picture not availble in your browser 38 | 39 | ## Sample results - 40 | Picture not availble in your browser 41 | Picture not availble in your browser 42 | Picture not availble in your browser 43 | Picture not availble in your browser 44 | 45 | 46 | The code may fail sometimes but tuning parameters of canny and appropriate dialation will improve results 47 | 48 | ### Data.zip contains all data I have used during implementation of this project 49 | -------------------------------------------------------------------------------- /images/.gitkeep: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /images/2018.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DevashishPrasad/Embossed-Text-Reader/87888c1f2a74ac72c33b3895f1f15b30293e0067/images/2018.png -------------------------------------------------------------------------------- /images/ger1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DevashishPrasad/Embossed-Text-Reader/87888c1f2a74ac72c33b3895f1f15b30293e0067/images/ger1.png -------------------------------------------------------------------------------- /images/ger2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DevashishPrasad/Embossed-Text-Reader/87888c1f2a74ac72c33b3895f1f15b30293e0067/images/ger2.png -------------------------------------------------------------------------------- /images/ger3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DevashishPrasad/Embossed-Text-Reader/87888c1f2a74ac72c33b3895f1f15b30293e0067/images/ger3.png -------------------------------------------------------------------------------- /images/ger4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DevashishPrasad/Embossed-Text-Reader/87888c1f2a74ac72c33b3895f1f15b30293e0067/images/ger4.png -------------------------------------------------------------------------------- /images/pro1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DevashishPrasad/Embossed-Text-Reader/87888c1f2a74ac72c33b3895f1f15b30293e0067/images/pro1.png -------------------------------------------------------------------------------- /images/pro2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DevashishPrasad/Embossed-Text-Reader/87888c1f2a74ac72c33b3895f1f15b30293e0067/images/pro2.png -------------------------------------------------------------------------------- /images/pro3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DevashishPrasad/Embossed-Text-Reader/87888c1f2a74ac72c33b3895f1f15b30293e0067/images/pro3.png -------------------------------------------------------------------------------- /picture_from_webcam.py: -------------------------------------------------------------------------------- 1 | import cv2 as cv2 2 | import numpy as np 3 | import imutils 4 | import pytesseract 5 | 6 | # Capture from web cam 7 | cap = cv2.VideoCapture(0) 8 | 9 | _,image = cap.read() 10 | # You can load a saved file from disk 11 | # image = cv2.imread('test13.jpg') 12 | img = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) 13 | img = cv2.GaussianBlur(img, (7,7), 0) 14 | 15 | # perform edge detection, then perform a dilation + erosion to 16 | # close gaps in between object edges 17 | edged = cv2.Canny(img, 20, 50)# Adjust these parameters according to your image 18 | dilate = cv2.dilate(edged, None, iterations=1) 19 | # We don't perform erosion, it completely depends on the image and need 20 | # erode = cv2.erode(dilate, None, iterations=1) 21 | 22 | # make an empty mask 23 | mask = np.ones(img.shape[:2], dtype="uint8") * 255 24 | 25 | # find contours 26 | cnts,hierarchy = cv2.findContours(edged.copy(), cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE) 27 | cnts = cnts[0] if imutils.is_cv2() else cnts[1] 28 | 29 | orig = img.copy() 30 | for c in cnts: 31 | # if the contour is not sufficiently large, ignore it 32 | if cv2.contourArea(c) < 200: 33 | cv2.drawContours(mask, [c], -1, 0, -1) 34 | 35 | x,y,w,h = cv2.boundingRect(c) 36 | 37 | # Filter and remove more contours according to your need 38 | if(w>h): 39 | cv2.drawContours(mask, [c], -1, 0, -1) 40 | 41 | # Remove those ignored contours 42 | newimage = cv2.bitwise_and(dilate.copy(), dilate.copy(), mask=mask) 43 | # Dilate again if necessary 44 | img2 = cv2.dilate(newimage, None, iterations=1) 45 | ret2,th1 = cv2.threshold(newimage,0,255,cv2.THRESH_BINARY_INV | cv2.THRESH_OTSU) 46 | 47 | # tesseract on the filtered image 48 | temp = pytesseract.image_to_string(th1) 49 | # Write output on the image 50 | cv2.putText(image,temp,(50,100),cv2.FONT_HERSHEY_SIMPLEX,1.0,(0,255,255),3) 51 | 52 | # Show the results 53 | cv2.imshow('Original image', cv2.resize(image,(640,480))) 54 | cv2.imshow('Dilated', cv2.resize(dilate,(640,480))) 55 | cv2.imshow('New Image', cv2.resize(newimage,(640,480))) 56 | cv2.imshow('Inverted Threshold', cv2.resize(th1,(640,480))) 57 | 58 | cv2.waitKey(0) 59 | cv2.destroyAllWindows() 60 | 61 | -------------------------------------------------------------------------------- /read_from_disk.py: -------------------------------------------------------------------------------- 1 | import cv2 2 | import numpy as np 3 | import imutils 4 | import pytesseract 5 | 6 | # read image from disk 7 | image = cv2.imread('test13.jpg') 8 | # make it gray 9 | img = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) 10 | # blur it to remove noise 11 | img = cv2.GaussianBlur(img, (7,7), 0) 12 | 13 | # perform edge detection, then perform a dilation + erosion to 14 | # close gaps in between object edges 15 | edged = cv2.Canny(img, 40, 90) 16 | dilate = cv2.dilate(edged, None, iterations=2) 17 | # perform erosion if necessay, it completely depends on the image 18 | # erode = cv2.erode(dilate, None, iterations=1) 19 | 20 | # create an empty masks 21 | mask = np.ones(img.shape[:2], dtype="uint8") * 255 22 | 23 | # find contours 24 | cnts = cv2.findContours(dilate.copy(), cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE) 25 | cnts = cnts[0] if imutils.is_cv2() else cnts[1] 26 | 27 | orig = img.copy() 28 | for c in cnts: 29 | # if the contour is not sufficiently large, ignore it 30 | if cv2.contourArea(c) < 300: 31 | cv2.drawContours(mask, [c], -1, 0, -1) 32 | 33 | x,y,w,h = cv2.boundingRect(c) 34 | 35 | # filter more contours if nessesary 36 | if(w>h): 37 | cv2.drawContours(mask, [c], -1, 0, -1) 38 | 39 | newimage = cv2.bitwise_and(dilate.copy(), dilate.copy(), mask=mask) 40 | img2 = cv2.dilate(newimage, None, iterations=3) 41 | ret2,th1 = cv2.threshold(img2 ,0, 255, cv2.THRESH_BINARY_INV | cv2.THRESH_OTSU) 42 | 43 | # Tesseract OCR on the image 44 | temp = pytesseract.image_to_string(th1) 45 | # Write results on the image 46 | cv2.putText(image, temp, (100,100), cv2.FONT_HERSHEY_SIMPLEX, 1.8, (0,255,255), 3) 47 | 48 | # show the outputs 49 | cv2.imshow('Original image', cv2.resize(image,(640,480))) 50 | cv2.imshow('Dilated', cv2.resize(dilate,(640,480))) 51 | cv2.imshow('New Image', cv2.resize(newimage,(640,480))) 52 | cv2.imshow('Inverted Threshold', cv2.resize(th1,(640,480))) 53 | 54 | cv2.waitKey(0) 55 | cv2.destroyAllWindows() 56 | 57 | -------------------------------------------------------------------------------- /video_from_webam.py: -------------------------------------------------------------------------------- 1 | import cv2 2 | import numpy as np 3 | import imutils 4 | import pytesseract 5 | 6 | # read from web camera 7 | cap = cv2.VideoCapture(1) 8 | 9 | while True: 10 | # Read a frame 11 | _,image = cap.read() 12 | img = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) 13 | img = cv2.GaussianBlur(img, (7,7), 0) 14 | 15 | # perform edge detection, then perform a dilation + erosion to 16 | # close gaps in between object edges 17 | edged = cv2.Canny(img, 40, 90) 18 | dilate = cv2.dilate(edged, None, iterations=2) 19 | # erode = cv2.erode(dilate, None, iterations=1) 20 | 21 | # create ann empty mask 22 | mask = np.ones(img.shape[:2], dtype="uint8") * 255 23 | 24 | # find the contours 25 | cnts,hierarchy = cv2.findContours(edged.copy(), cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE) 26 | cnts = cnts[0] if imutils.is_cv2() else cnts[1] 27 | 28 | orig = img.copy() 29 | for c in cnts: 30 | # if the contour is not sufficiently large, ignore it 31 | if cv2.contourArea(c) < 200: 32 | cv2.drawContours(mask, [c], -1, 0, -1) 33 | 34 | x,y,w,h = cv2.boundingRect(c) 35 | 36 | # Filter and remove more contours according to your need 37 | if(w>h): 38 | cv2.drawContours(mask, [c], -1, 0, -1) 39 | 40 | # Remove ignored contours 41 | newimage = cv2.bitwise_and(dilate.copy(), dilate.copy(), mask=mask) 42 | # Dialte agin if necessay 43 | img2 = cv2.dilate(newimage, None, iterations=1) 44 | # Invert threshold for better results 45 | ret2,th1 = cv2.threshold(img2 ,0,255,cv2.THRESH_BINARY_INV | cv2.THRESH_OTSU) 46 | 47 | # Tesseract on the filtered image 48 | temp = pytesseract.image_to_string(th1) 49 | # Write text on the image 50 | cv2.putText(image,temp,(50,100),cv2.FONT_HERSHEY_SIMPLEX,1.0,(0,255,255),3) 51 | 52 | # Show the results 53 | cv2.imshow('Original image', cv2.resize(image,(640,480))) 54 | cv2.imshow('Dilated', cv2.resize(dilate,(640,480))) 55 | cv2.imshow('New Image', cv2.resize(newimage,(640,480))) 56 | cv2.imshow('Inverted Threshold', cv2.resize(th1,(640,480))) 57 | 58 | # Exit condition 59 | if cv2.waitKey(1) == ord('q'): 60 | break 61 | 62 | cv2.destroyAllWindows() 63 | 64 | --------------------------------------------------------------------------------