如何在計算機上輔助計算公式?目前的方案主要包括mathOCR、愛做業、做業幫等表明,以下:
node
它們的缺點主要包括:算法
不支持手寫字符輸入、
不支持複雜題型、
不支持題庫外的式子等等,
對於複雜的公式難以識別,對於簡單的公式則應用場景很少。express所以咱們但願解決上述痛點,加強產品功能。
此類產品主要針對有輔助計算需求的學者和論文寫做者,但還有一個基本的要求是近年來不斷升級的驗證碼識別,以算術形式出現的驗證碼每每由被修改過的字符以異常的形式排列,所以不要求識別工具對於複雜式子的識別,但有對識別準確率的要求。算式識別工具能夠做爲API接口提供服務,便於爬蟲等須要自動訪問網頁的工具調用,提升對算術驗證碼的識別、經過能力。編程
**識別目標:對於四則運算+-*/ 和分式具備識別和計算能力**canvas
- 架構設計:
- pipeline處理,流程以下
- gui讀取用戶輸入圖片
- 作圖像預處理,包括二值處理、濾波、切割
- 送進CNN識別
- 結合每一個字符的識別結果和位置關係捆綁空間上不連續的字符造成符號(切割時按邊界切割)
- 按定義好的支持文法實現遞歸降低語法制導翻譯,對輸入記號流遞歸獲得求解結果
- 展現在gui上
- 圖片預處理
- 圖片預處理以OpenCV做爲主要工具。預處理的主要目的是把圖片中的字符切割出來,同時避免無關變量對字符識別的影響。
- 主要步驟包括:灰度化、二值化、高斯濾波、字符切割
工具介紹:微信
- 卷積神經網絡模型(CNN)
- 國際數學公式識別比賽數據集(CROHME)
海量字符集圖片
與實際輸入類似
原圖
網絡
binary image
架構
extracted components
app
項目重要文件介紹:框架
操做說明:
測試樣例:
優勢
缺點
是否有充足的時間來作計劃?
團隊在計劃階段是如何解決同事們對於計劃的不一樣意見的?
你原計劃的工做是否最後都作完了? 若是有沒作完的,爲何?
是否項目的整個過程都按照計劃進行,項目出了什麼意外?有什麼風險是當時沒有估計到的,爲何沒有估計到?
咱們學到了什麼? 若是歷史重來一遍, 咱們會作什麼改進?
咱們有足夠的資源來完成各項任務麼?
測試的時間,人力和軟件/硬件資源是否足夠? 對於那些不須要編程的資源 (美工設計/文案)是否低估難度?
每一個相關的成員都及時知道了變動的消息?
咱們採用了什麼辦法決定「推遲」和「必須實現」的功能?
成員是否可以有效地處理意料以外的工做請求?
設計工做在何時,由誰來完成的?是合適的時間,合適的人麼?
設計工做有沒有碰到模棱兩可的狀況,團隊是如何解決的?
什麼功能產生的Bug最多,爲何?在發佈以後發現了什麼重要的bug? 爲何咱們在設計/開發的時候沒有想到這些狀況?
import solver from PIL import Image from kivy.app import App from kivy.uix.boxlayout import BoxLayout from kivy.uix.button import Button from kivy.uix.label import Label from kivy.uix.widget import Widget from kivy.graphics import Color, Line class PaintWidget(Widget): color = (254, 0, 0, 1) # Pen color 畫筆顏色 thick = 8 # Pen thickness 畫筆粗度 def __init__(self, root, **kwargs): super().__init__(**kwargs) self.parent_widget = root # Touch down motion: # If the touch position is located in the painting board, draw lines. # 按下動做: # 若是觸摸位置在畫板內,則在畫板上劃線 def on_touch_down(self, touch): with self.canvas: Color(*self.color, mode='rgba') if touch.x > self.width or touch.y < self.parent_widget.height - self.height: return touch.ud['line'] = Line(points=(touch.x, touch.y), width=self.thick) # Touch move motion: # Draw line with mouse/hand moving # 移動動做: # 隨着鼠標/手指的移動畫線 def on_touch_move(self, touch): with self.canvas: if touch.x > self.width or touch.y < self.parent_widget.height - self.height: return touch.ud['line'].points += [touch.x, touch.y] # Touch up motion: # When ending drawing line, save the picture, and call the prediction component to do prediction # 擡起動做: # 結束畫線,保存圖片成文件,並調用預測相關的組件作預測 def on_touch_up(self, touch): if touch.x > self.width or touch.y < self.parent_widget.height - self.height: return #self.parent.parent.do_predictions() def export_image(self): input_img_name = './input_expression.png' self.export_to_png(input_img_name) im = Image.open(input_img_name) x, y = im.size p = Image.new('RGBA', im.size, (255, 255, 255)) p.paste(im, (0, 0, x, y), im) p.save('white_bg.png') return 'white_bg.png' class Recognizer(BoxLayout): def __init__(self, **kwargs): super().__init__(**kwargs) self.number = -1 # Variable to store the predicted number 保存識別的數字的變量 self.orientation = 'horizontal' # UI related UI相關 self.draw_window() # function to declare the components of the application, and add them to the window # 聲明程序UI組件的函數,而且將它們添加到窗口上 def draw_window(self): # Clear button 清除按鈕 self.clear_button = Button(text='CLEAR', font_name=HandwrittenMathCalculator.font_name, size_hint=(1, 4 / 45), background_color=(255, 165 / 255, 0, 1)) self.solve_button = Button(text='SOLVE', font_name=HandwrittenMathCalculator.font_name, size_hint=(1, 4 / 45), background_color=(255, 165 / 255, 0, 1)) # Painting board 畫板 self.painter = PaintWidget(self, size_hint=(1, 8 / 9)) # Label for hint text 提示文字標籤 self.hint_label = Label(font_name=HandwrittenMathCalculator.font_name, size_hint=(1, 1 / 45)) # Label for predicted number 識別數字展現標籤 self.result_label = Label(font_size=120, size_hint=(1, 1 / 3)) # Label for some info 展現一些信息的標籤 self.info_board = Label(font_size=24, size_hint=(1, 22 / 45)) # BoxLayout 盒子佈局 first_column = BoxLayout(orientation='vertical', size_hint=(2 / 3, 1)) second_column = BoxLayout(orientation='vertical', size_hint=(1 / 3, 1)) # Add widgets to the window 將各個組件加到應用窗口上 first_column.add_widget(self.painter) first_column.add_widget(self.hint_label) second_column.add_widget(self.result_label) second_column.add_widget(self.info_board) second_column.add_widget(self.solve_button) second_column.add_widget(self.clear_button) self.add_widget(first_column) self.add_widget(second_column) # motion binding 動做綁定 # Bind the click of the clear button to the clear_paint function # 將清除按鈕的點擊事件綁定到clear_paint函數上 self.clear_button.bind(on_release=self.clear_paint) self.solve_button.bind(on_release=self.solve_expression) self.clear_paint() # Initialize the state of the app 初始化應用狀態 # Clear the painting board and initialize the state of the app. def clear_paint(self): self.painter.export_image() #call solver to solve # Clear the painting board and initialize the state of the app. def clear_paint(self, obj=None): self.painter.canvas.clear() self.number = -1 self.result_label.text = '?' self.hint_label.text = 'Write math expression above' self.info_board.text = 'Detected expression:\n' # Extract info from the predictions, and display them on the window # 從預測結果中提取信息,並展現在窗口上 def show_info(self, result, detected_expression='8+7'): if result == None: self.number = 'Error' else: self.number = result self.result_label.text = str(self.number) self.hint_label.text = 'Detected expression and result is shown.Press clear to Retry!' self.info_board.text += detected_expression def solve_expression(self, obj=None): img = self.painter.export_image() self.info_board.text = 'Detected expression:\n' (result,detected_expression) = solver.solve(img) self.show_info(result,detected_expression) # Main app class # 主程序類 class HandwrittenMathCalculator(App): font_name = r'Arial.ttf' def build(self): return Recognizer()
def solve(filename,mode = 'product'): original_img, binary_img = read_img_and_convert_to_binary(filename) symbols = binary_img_segment(binary_img, original_img) sort_symbols = sort_characters(symbols) process.detect_uncontinous_symbols(sort_symbols, binary_img) length = len(symbols) symbols_to_be_predicted = normalize_matrix_value([x['src_img'] for x in symbols]) predict_input_fn = tf.estimator.inputs.numpy_input_fn( x={"x": np.array(symbols_to_be_predicted)}, shuffle=False) predictions = cnn_symbol_classifier.predict(input_fn=predict_input_fn) characters = [] for i,p in enumerate(predictions): # print(p['classes'],FILELIST[p['classes']]) candidates = get_candidates(p['probabilities']) characters.append({'location':symbols[i]['location'],'candidates':candidates}) #print([x['location'] for x in characters]) modify_characters(characters) # print('排序後的字符序列') # print([[x['location'], x['candidates']] for x in characters]) tokens = process.group_into_tokens(characters) # print('識別出的token') print(tokens) node_list = characters_to_nodes(characters) print(node_list) exp_parser = Exp_parser() result=exp_parser.expression(node_list) str = '' for token in tokens: str += token['token_string'] print(result) if result is None: return None, str else: return (round(result,2),str)