Python識(shí)別快遞條形碼及Tesseract-OCR使用詳解
識(shí)別快遞單號(hào)
這次跟老師做項(xiàng)目,這項(xiàng)目大概是流水線(xiàn)上識(shí)別快遞上的快遞單號(hào)。首先我嘗試了解條形碼的基本知識(shí)
百度百科:條形碼
條形碼(barcode)是將寬度不等的多個(gè)黑條和空白,按照一定的編碼規(guī)則排列,用以表達(dá)一組信息的圖形標(biāo)識(shí)符。常見(jiàn)的條形碼是由反射率相差很大的黑條(簡(jiǎn)稱(chēng)條)和白條(簡(jiǎn)稱(chēng)空)排成的平行線(xiàn)圖案。條形碼可以標(biāo)出物品的生產(chǎn)國(guó)、制造廠家、商品名稱(chēng)、生產(chǎn)日期、圖書(shū)分類(lèi)號(hào)、郵件起止地點(diǎn)、類(lèi)別、日期等許多信息,因而在商品流通、圖書(shū)管理、郵政管理、銀行系統(tǒng)等許多領(lǐng)域都得到廣泛的應(yīng)用。
條形碼有多種,在我國(guó)廣泛流傳的是EAN13條形碼(以下簡(jiǎn)稱(chēng)條形碼),所以主要研究該種條形碼的識(shí)別。
條形碼位數(shù)說(shuō)明:
- 條形碼一共有13位
- 前2位或者前3位稱(chēng)為前綴,表示國(guó)家、地區(qū)或者某種特定的商品類(lèi)型
- 中國(guó)區(qū)條形碼開(kāi)頭:690~699
- 圖書(shū)類(lèi)條形碼開(kāi)頭:978~979
- 前綴后的4位或者5位稱(chēng)為廠商代碼,表示產(chǎn)品制造商
- 廠商代碼后5位稱(chēng)為商品代碼,表示具體的商品項(xiàng)目
- 最后1位是校驗(yàn)碼,根據(jù)前12位計(jì)算而出,可以用來(lái)防偽以及識(shí)別校驗(yàn)
條形碼編碼說(shuō)明
條形碼一共有8個(gè)區(qū)域:左側(cè)空白區(qū)->起始符->左側(cè)數(shù)據(jù)符->中間分隔符->右側(cè)數(shù)據(jù)符->校驗(yàn)符->終止符->右側(cè)空白區(qū)
- 字符為0~9
- 除空白區(qū)外的區(qū)域和字符都采用二進(jìn)制編碼表示,1表示bar(黑條),0表示space(白條)
- 起始符,終止符編碼為101,分隔符編碼為01010
- 0~9每種字符有3種編碼方式,AB為左側(cè)數(shù)據(jù)奇偶編碼,C為右側(cè)數(shù)據(jù)偶編碼
- 左側(cè)數(shù)據(jù)的奇偶性由前置符決定(就是說(shuō),第一個(gè)支付是幾就按下面的排列開(kāi)始)
還有這么一種理解編碼方法
以寬度為編碼,去掉起始碼,終止碼,中間分隔碼,不管白條還是黑條都算一個(gè)編碼,最窄一節(jié)為1(最窄的為單位寬度),兩個(gè)單位寬度就是2,三單位長(zhǎng)度為3,四單位寬度為4
四條(不管黑條還是白條都算條)代表一個(gè)數(shù)字
四條長(zhǎng)度 | 數(shù)字 |
---|---|
3211 | 0 |
2221 | 1 |
2122 | 2 |
1411 | 3 |
1132 | 4 |
1231 | 5 |
1114 | 6 |
1312 | 7 |
1213 | 8 |
3112 | 9 |
兩種編碼的圖示
這就代表為 數(shù)字 1
校驗(yàn)
EAN13條形碼一共有13位,最后1位是校驗(yàn)位,該位是通過(guò)前12位按照一定的步驟計(jì)算出來(lái)的。
如果按照一定的步驟處理識(shí)別出的前12位數(shù)據(jù),如果計(jì)算結(jié)果和識(shí)別出的結(jié)果相等,識(shí)別正確;
如果不相等,則重新識(shí)別或糾錯(cuò)再校驗(yàn)或提示識(shí)別失敗。
校驗(yàn)碼計(jì)算方法
以下圖所示的條形碼舉例說(shuō)明:
條形碼的位數(shù)起始位為最右一位,即校驗(yàn)位,檢驗(yàn)碼計(jì)算方法如下:
- 偶位數(shù)數(shù)值相加乘3((0+2+0+8+1+9)*3=60)
- 不含校驗(yàn)位的奇位數(shù)相加(7+4+7+9+3+6=36)
- 將前兩步的結(jié)果相加(60+36=96)
- 用10減去上一步結(jié)果的個(gè)位數(shù)數(shù)值(10-6=4)
- 上一步結(jié)果的個(gè)位數(shù)即為校驗(yàn)碼(4)
源碼
#創(chuàng)建:2016/01/26 #文件:BarCodeIdentification.py #作者:moverzp #功能:識(shí)別條形碼 import sys import cv2 DECODING_TABLE = { '0001101': 0, '0100111': 0, '1110010': 0, '0011001': 1, '0110011': 1, '1100110': 1, '0010011': 2, '0011011': 2, '1101100': 2, '0111101': 3, '0100001': 3, '1000010': 3, '0100011': 4, '0011101': 4, '1011100': 4, '0110001': 5, '0111001': 5, '1001110': 5, '0101111': 6, '0000101': 6, '1010000': 6, '0111011': 7, '0010001': 7, '1000100': 7, '0110111': 8, '0001001': 8, '1001000': 8, '0001011': 9, '0010111': 9, '1110100': 9, } EDGE_TABLE = { 2:{2:6,3:0,4:4,5:3}, 3:{2:9,3:'33',4:'34',5:5}, 4:{2:9,3:'43',4:'44',5:5}, 5:{2:6,3:0,4:4,5:3}, } INDEX_IN_WIDTH = (0, 4, 8, 12, 16, 20, 24, 33, 37, 41, 45, 49, 53) def get_bar_space_width(img): row = img.shape[0] *1/2 currentPix = -1 lastPix = -1 pos = 0 width = [] for i in range(img.shape[1]):#遍歷一整行 currentPix = img[row][i] if currentPix != lastPix: if lastPix == -1: lastPix = currentPix pos = i else: width.append( i - pos ) pos = i lastPix = currentPix return width def divide(t, l): if float(t) / l < 0.357: return 2 elif float(t) / l < 0.500: return 3 elif float(t) / l < 0.643: return 4 else: return 5 def cal_similar_edge(data): similarEdge = [] #先判斷起始符 limit = float(data[1] + data[2] + data[3] ) / 3 * 1.5 if data[1] >= limit or data[2] >= limit or data[3] >= limit: return -1#寬度提取失敗 index = 4 while index < 54: #跳過(guò)分隔符區(qū)間 if index==28 or index==29 or index==30 or index==31 or index==32: index +=1 continue #字符檢測(cè) T1 = data[index] + data[index+1] T2 = data[index+1] + data[index+2] L = data[index] + data[index+1] + data[index+2] + data[index+3] similarEdge.append( divide(T1, L) ) similarEdge.append( divide(T2, L) ) index += 4 return similarEdge def decode_similar_edge(edge): barCode = [6]#第一個(gè)字符一定是6,中國(guó)區(qū) for i in range (0, 24, 2):#每個(gè)字符兩個(gè)相似邊,共12個(gè)字符 barCode.append( EDGE_TABLE[edge[i]][edge[i+1]] ) return barCode def decode_sharp(barCode, barSpaceWidth): for i in range(0, 13): if barCode[i] == '44': index = INDEX_IN_WIDTH[i] c3 = barSpaceWidth[index+2] c4 = barSpaceWidth[index+3] if c3 > c4: barCode[i] = 1 else: barCode[i] = 7 elif barCode[i] == '33': index = INDEX_IN_WIDTH[i] c1 = barSpaceWidth[index] c2 = barSpaceWidth[index+1] if c1 > c2: barCode[i] = 2 else: barCode[i] = 8 elif barCode[i] == '34': index = INDEX_IN_WIDTH[i] c1 = barSpaceWidth[index] c2 = barSpaceWidth[index+1] if c1 > c2: barCode[i] = 7 else: barCode[i] = 1 elif barCode[i] == '43': index = INDEX_IN_WIDTH[i] c2 = barSpaceWidth[index+1] c3 = barSpaceWidth[index+2] if c2 > c3: barCode[i] = 2 else: barCode[i] = 8 def check_bar_code(barCode): evens = barCode[11]+barCode[9]+barCode[7]+barCode[5]+barCode[3]+barCode[1] odds = barCode[10]+barCode[8]+barCode[6]+barCode[4]+barCode[2]+barCode[0] sum = evens * 3 + odds if barCode[12] == (10 - sum % 10) % 10: return True else: return False #載入圖像 img = cv2.imread('res\google6.jpg') grayImg = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)#轉(zhuǎn)換成單通道圖像 ret, grayImg = cv2.threshold(grayImg, 200, 255, cv2.THRESH_BINARY)#二值化 grayImg = cv2.medianBlur(grayImg, 3)#中值濾波 #提取條空寬度 barSpaceWidth = get_bar_space_width(grayImg) print 'bar & space\'s numbers:', len(barSpaceWidth)#只有60是正確的 print barSpaceWidth #計(jì)算相似邊數(shù)值 similarEdge = cal_similar_edge(barSpaceWidth) if similarEdge == -1: print 'barSpaceWidth error!' sys.exit() print 'similarEdge\'s numbers:', len(similarEdge) print similarEdge #相似邊譯碼 barCode = decode_similar_edge(similarEdge) #針對(duì)‘#'譯碼 decode_sharp(barCode, barSpaceWidth) #校驗(yàn) valid = check_bar_code(barCode) valid = 1 print 'barcode:\n', barCode if valid else 'Check barcode error!' height = img.shape[0] width = img.shape[1] cv2.line(grayImg, (0, height/2), (width, height/2),(0, 255, 0), 2)#畫(huà)出掃描的行 #顯示圖像 cv2.imshow("origin", img) cv2.imshow("result", grayImg) key = cv2.waitKey(0) if key == 27: cv2.destroyAllWindows()
第二種編碼的程序
#-*- coding:utf-8 -*- from PIL import Image def clean(img): A = img.load() print A ss = '' for x in xrange(img.size[0]): ss += str(A[x, img.size[1]/2]) print ss ls = [] while len(ss) > 0: start = ss[0] j = 1 while j < len(ss) and ss[j] == start : j += 1 ls.append(j) ss = ss[j:] print ls return ls #print ls def GetUPC_A(t): #print t t = t[4:-4] print len(t) for i in xrange(len(t)): t[i] = (t[i] + 1) / 4 t = t[:24] + t[29:] s = '' for i in xrange(len(t)): s += str(t[i]) upca = '' for i in range(0, len(s) / 4): n = i * 4 upca += dic[s[n:n + 4]] print upca dic = {'3211':'0', '2221':'1', '2122':'2', '1411':'3', '1132':'4', '1231':'5', '1114':'6', '1312':'7', '1213':'8', '3112':'9'} img = Image.open('7.png') GetUPC_A(clean(img))
可惜這次遇到的是快遞單上的條形碼,非標(biāo)準(zhǔn)的EAN13條形碼,暫時(shí)還不清楚這條形碼的編碼方式,所以換一個(gè)思路來(lái)識(shí)別快遞單號(hào),直接識(shí)別快遞單上的數(shù)字快遞單號(hào)
這里我用OCR引擎來(lái)識(shí)別,用的是Tesseract-OCR引擎
Tesseract-OCR引擎簡(jiǎn)介
OCR(Optical Character Recognition):光學(xué)字符識(shí)別,是指對(duì)圖片文件中的文字進(jìn)行分析識(shí)別,獲取的過(guò)程。Tesseract的OCR引擎最先由HP實(shí)驗(yàn)室于1985年開(kāi)始研發(fā),至1995年時(shí)已經(jīng)成為OCR業(yè)內(nèi)最準(zhǔn)確的三款識(shí)別引擎之一。然而,HP不久便決定放棄OCR業(yè)務(wù),Tesseract也從此塵封。
數(shù)年以后,HP意識(shí)到,與其將Tesseract束之高閣,不如貢獻(xiàn)給開(kāi)源軟件業(yè),讓其重?zé)ㄐ律?005年,Tesseract由美國(guó)內(nèi)華達(dá)州信息技術(shù)研究所獲得,并求諸于Google對(duì)Tesseract進(jìn)行改進(jìn)、消除Bug、優(yōu)化工作。
(由Google管理,所以下載地址“被墻”了,這里就不貼了)
還有一個(gè)模塊就是 pytesseract 這包是對(duì)Google Tesseract的一層python封裝需要配合 PIL 模塊使用
所以此次識(shí)別快遞單號(hào),用到三個(gè)
- Tesseract-OCR ——(直接下載一個(gè)exe文件一路”next”即可安裝完成)
- pytesseract模塊——(直接 pip install pytesseract 安裝即可)
- PIL模塊——(由于我的是win7_64bit的系統(tǒng),原PIL不支持,所以用pillow模塊,直接pip install pillow即可)
源代碼
#-*- coding:utf-8 -*- from PIL import Image import pytesseract import time start = time.clock()#開(kāi)始計(jì)時(shí) #---------主要代碼------------ im = Image.open('66.png') code = pytesseract.image_to_string(im) print u'驗(yàn)證碼:' + str(code) #--------------------------------- end = time.clock()#結(jié)束計(jì)時(shí) print u'運(yùn)行時(shí)間:' + str(end-start)
有坑
在有 Git Bash調(diào)試時(shí)遇到了
Traceback (most recent call last): File "111.py", line 10, in <module> print u'驗(yàn)證碼:' + str(code) UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-2: ordinal not in range(128)
這一看就有事編碼的坑了,我用的是python2.7 估計(jì)生3就沒(méi)沒(méi)坑了
但目前還是要解決這問(wèn)題,對(duì)于這編碼的問(wèn)題有兩種解決方法:
1.一個(gè)解決的方案在程序中加入以下代碼:
import sys reload(sys) sys.setdefaultencoding('utf8')
2.是在python的Lib\site-packages文件夾下新建一個(gè)sitecustomize.py,內(nèi)容為:
# encoding=utf8 import sys reload(sys) sys.setdefaultencoding('utf8')
此時(shí)重啟python解釋器,執(zhí)行sys.getdefaultencoding(),發(fā)現(xiàn)編碼已經(jīng)被設(shè)置為utf8的了,多次重啟之后,效果相同,這是因?yàn)橄到y(tǒng)在python啟動(dòng)的時(shí)候,自行調(diào)用該文件,設(shè)置系統(tǒng)的默認(rèn)編碼,而不需要每次都手動(dòng)的加上解決代碼,屬于一勞永逸的解決方法。
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- Selenium+Tesseract-OCR智能識(shí)別驗(yàn)證碼爬取網(wǎng)頁(yè)數(shù)據(jù)的實(shí)例
- Java使用Tesseract-Ocr識(shí)別數(shù)字
- python3使用Pillow、tesseract-ocr與pytesseract模塊的圖片識(shí)別的方法
- Python3.6使用tesseract-ocr的正確方法
- Perl使用Tesseract-OCR實(shí)現(xiàn)驗(yàn)證碼識(shí)別教程
- python利用 pytesseract快速識(shí)別提取圖片中的文字((圖片識(shí)別)
- 使用Python中的pytesseract模塊實(shí)現(xiàn)抓取圖片中文字
- opencv+tesseract實(shí)現(xiàn)驗(yàn)證碼識(shí)別的示例
- C#使用Tesseract進(jìn)行Ocr識(shí)別的方法實(shí)現(xiàn)
- tesseract-ocr使用以及訓(xùn)練方法
相關(guān)文章
python實(shí)戰(zhàn)教程之OCR文字識(shí)別方法匯總
ocr是一種光學(xué)字符識(shí)別技術(shù),簡(jiǎn)單來(lái)說(shuō)它能夠識(shí)別出圖像中的文字并且將其給取出來(lái),下面這篇文章主要給大家介紹了關(guān)于python實(shí)戰(zhàn)教程之OCR文字識(shí)別方法的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),需要的朋友可以參考下2023-05-05關(guān)于CUDA out of memory的解決方案
這篇文章主要介紹了關(guān)于CUDA out of memory的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-02-02基于python解線(xiàn)性矩陣方程(numpy中的matrix類(lèi))
這篇文章主要介紹了基于python解線(xiàn)性矩陣方程(numpy中的matrix類(lèi)),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-10-10修改 CentOS 6.x 上默認(rèn)Python的方法
這篇文章主要介紹了修改 CentOS 6.x 上默認(rèn)Python的方法,本文給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2019-09-09用pickle存儲(chǔ)Python的原生對(duì)象方法
下面小編就為大家?guī)?lái)一篇用pickle存儲(chǔ)Python的原生對(duì)象方法。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-04-04Python?Math數(shù)學(xué)函數(shù)常數(shù)冪和對(duì)數(shù)基礎(chǔ)應(yīng)用實(shí)例
Python中的math模塊是數(shù)學(xué)運(yùn)算的重要工具,提供了豐富的數(shù)學(xué)函數(shù)和常數(shù),本文將深入探討math模塊的功能和用法,使您能夠更好地利用Python進(jìn)行數(shù)學(xué)運(yùn)算2023-12-12詳解如何使用Pandas處理時(shí)間序列數(shù)據(jù)
時(shí)間序列數(shù)據(jù)在數(shù)據(jù)分析建模中很常見(jiàn),例如天氣預(yù)報(bào),空氣狀態(tài)監(jiān)測(cè),股票交易等金融場(chǎng)景,本文給大家詳細(xì)介紹了如何使用Pandas處理時(shí)間序列數(shù)據(jù),文中通過(guò)代碼示例講解的非常詳細(xì),需要的朋友可以參考下2024-01-01python 實(shí)現(xiàn)logging動(dòng)態(tài)變更輸出日志文件名
這篇文章主要介紹了python 實(shí)現(xiàn)logging動(dòng)態(tài)變更輸出日志文件名的案例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2021-03-03