python實現ocr
python實現ocr
前期準備
在這個階段主要準備整個小程式的結構,既然要實現ocr,那麼輸入就是一張圖片,而圖片這裡採用螢幕截圖的方式獲得,輸出是文字,這裡採用搜狗的ocr介面,我們把截好的圖片傳到搜狗ocr介面中,然後把返回的文字作為輸出即可。
由於想做一個小程式,所以要為程式做GUI,這裡採用tkinter編制GUI介面。
介面編寫
介面主要就準備一個窗體,裡面有選單,給出OCR功能。
之後我們點選選單,則啟動一個截圖功能,在截圖完成後,我們就把截得的圖片傳入ocr介面並返回文字到主窗體中。
主面板的編寫則直接使用tkinter建立選單等
root = Tk() root.title("小新的OCR") # 建立一個頂級選單 menubar = Menu(root) # 建立一個下拉選單“檔案”,然後將它新增到頂級選單中 filemenu = Menu(menubar, tearoff=False) filemenu.add_command(label="OCR", command=buttonCaptureClick, accelerator='Ctrl+N') filemenu.add_command(label="幫助",command=helpClick) filemenu.add_command(label="退出", command=root.quit) menubar.add_cascade(label="操作", menu=filemenu) # 顯示選單 root.config(menu=menubar) root.bind_all("<Control-d>", lambda event: buttonCaptureClick()) #啟動訊息主迴圈 root.mainloop()
這樣變回產生一個窗體,使用者可以和這個窗體進行互動,你可以點選選單,然後找到其子選單中的OCR一項,點選它便會呼叫一個buttonCaptureClick的函式,這個函式就來產生截圖,並且儲存截圖。
截圖功能實現
截圖功能我也是參考網上的內容,原理就是先把整個螢幕給捕捉到,然後監聽滑鼠事件,當滑鼠左邊按下則作為截圖的左頂點,滑鼠左鍵松下則最為截圖的右底點,這樣我們截圖區域就出來了,然後進行儲存即可。
#用來顯示全螢幕截圖並響應二次截圖的視窗類 class MyCapture: def __init__(self, png): #變數X和Y用來記錄滑鼠左鍵按下的位置 self.X = tkinter.IntVar(value=0) self.Y = tkinter.IntVar(value=0) #螢幕尺寸 screenWidth = root.winfo_screenwidth() screenHeight = root.winfo_screenheight() #建立頂級元件容器 self.top = tkinter.Toplevel(root, width=screenWidth, height=screenHeight) #不顯示最大化、最小化按鈕 self.top.overrideredirect(True) self.canvas = tkinter.Canvas(self.top,bg='white', width=screenWidth, height=screenHeight) #顯示全屏截圖,在全屏截圖上進行區域截圖 self.image = tkinter.PhotoImage(file=png) self.text ="" self.canvas.create_image(screenWidth//2, screenHeight//2, image=self.image) #滑鼠左鍵按下的位置 def onLeftButtonDown(event): self.X.set(event.x) self.Y.set(event.y) #開始截圖 self.sel = True self.canvas.bind('<Button-1>', onLeftButtonDown) #滑鼠左鍵移動,顯示選取的區域 def onLeftButtonMove(event): if not self.sel: return global lastDraw try: #刪除剛畫完的圖形,要不然滑鼠移動的時候是黑乎乎的一片矩形 self.canvas.delete(lastDraw) except Exception as e: pass lastDraw = self.canvas.create_rectangle(self.X.get(), self.Y.get(), event.x, event.y, outline='black') self.canvas.bind('<B1-Motion>', onLeftButtonMove) #獲取滑鼠左鍵擡起的位置,儲存區域截圖 def onLeftButtonUp(event): self.sel = False try: self.canvas.delete(lastDraw) except Exception as e: pass sleep(0.1) #考慮滑鼠左鍵從右下方按下而從左上方擡起的截圖 left, right = sorted([self.X.get(), event.x]) top, bottom = sorted([self.Y.get(), event.y]) pic = ImageGrab.grab((left+1, top+1, right, bottom)) fileName ="temp.jpg" pic.save(fileName) self.text = get_text(fileName) #關閉當前視窗 self.top.destroy() self.canvas.bind('<ButtonRelease-1>', onLeftButtonUp) #讓canvas充滿視窗,並隨視窗自動適應大小 self.canvas.pack(fill=tkinter.BOTH, expand=tkinter.YES) #開始截圖 def buttonCaptureClick(): #最小化主視窗 # root.state('icon') root.withdraw() sleep(0.4) filename = 'temp.png' #grab()方法預設對全螢幕進行截圖 im = ImageGrab.grab() im.save(filename) im.close() #顯示全螢幕截圖 w = MyCapture(filename) root.wait_window(w.top) #截圖結束,恢復主視窗,並刪除臨時的全螢幕截圖檔案 root.update() root.deiconify() text1.config(state = NORMAL) text1.delete(0.0,END) text1.insert('insert',w.text) text1.config(state = DISABLED) text1.pack() os.remove(filename)
OCR實現
因為OCR其實是採用了搜狗的介面,所以需要做的工作也不是很多,只需要把我們的圖片傳入即可。
def get_text(img_path):
print("")
img = img_path # 圖片路徑
files = {"pic_path": open(img, "rb")} # files # 類似data資料
url = "http://pic.sogou.com/pic/upload_pic.jsp" # post的url
keywords = requests.post(url, files=files).text # requests 提交圖片
url = "http://pic.sogou.com/pic/ocr/ocrOnline.jsp?query=" + keywords # keywords就是圖片url此方式為get請求
ocrResult = requests.get(url).json() # 直接轉換為json格式
contents = ocrResult['result'] # 類似字典 把result的value值取出來 是一個list然後裡面很多json就是識別的文字
text = ""
for content in contents: # 遍歷所有結果
text+=(content['content'].strip()+'\n') # strip去除空格 他返回的結果自帶一個換行
return text
內容顯示
內容顯示是在截圖結束後我們把ocr識別的內容儲存起來
self.text = get_text(fileName)
然後再顯示到主窗體上
text1.config(state = NORMAL)
text1.delete(0.0,END)
text1.insert('insert',w.text)
text1.config(state = DISABLED)
text1.pack()
總結
雖然是一個完整的專案,但是其中的很多模組其實都是借用其他人的模組,而我做的只是把他們結合起來做成一個小專案,所以是站在巨人的肩膀上開發。
參考:
https://cloud.tencent.com/developer/article/1097904
https://morvanzhou.github.io/tutorials/python-basic/tkinter/
https://www.52pojie.cn/thread-708177-1-1.html