辨識手寫英文單字

辨識手寫英文單字

在本次流程說明中,我們會利用威盛Pixetto 的手寫字母辨識功能,實作一個能辨識英文單字的 Python 程式。

在手寫字母識別功能裡,威盛Pixetto不僅能偵測英文字母,它也能告訴使用者各個字母的出現位置。透過以上資訊,我們可以精確地辨識使用者提供的手寫單字。接下來我們一步一步介紹如何匯入威盛Pixetto 所需函式庫、如何讀取並利用威盛Pixetto的回傳數據,達成本次實作目標。

若您尚未建立 Python 編程環境,請先參考 “Python 環境設置流程說明”。

步驟 1

將威盛Pixetto連結至電腦後,開啟 Pixetto Utility。在右方選單中選擇 “手寫字母辨識”。由於我們需要同時辨識多個字母,請將 “最大顯示物件數量” 設為5。

完成後即可測試 威盛Pixetto 是否能正確辨識字母。

步驟 2

開啟 Jupyter Notebook,並建立一份“ipynb”檔案。

在第一格 code cell 裏,匯入 pixetto 以及 collections 等函式庫。

from pixetto import Pixetto
from collections import defaultdict, Counter

在 Python 程式裏,我們會將威盛 Pixetto 視為一個 “類別” ( Class )。在後續的步驟中,我們可以直接呼叫類別中的 “方法” ( Method ),以開啟、關閉、或讀取威盛 Pixetto。

第二行的 “defaultdict” 以及 “Counter” 是處理資料時常用的工具。在之後的步驟,我們會對這兩種工具進行更詳盡的介紹。

步驟 3

在本次實作中,我們需要建立一個單字庫,讓程式認識英文單字。之後的步驟,我們會讓程式在偵測到正確的英文單字後停止。

在這個步驟中,我們需要用到 “Counter” 函式。 “Counter” 會讀取傳入的陣列,並以陣列中的元素為鍵 ( Key ),各元素在陣列中出現的次數為值( Value ),轉換為字典 ( Dictionary )。

請先到網站 ( https://norvig.com/big.txt ) 下載文字檔至您的資料夾下。接著,讓 Python 讀取檔案,將所有文字以空格作為區隔後,存入 “words” 陣列。最後利用 “Counter”,將陣列 “words” 轉換為字典 ( Dictionary ) “WORDS”。

import re
text = open('big.txt').read()
words = re.findall(r'\w+', text.lower())
WORDS = Counter(words)

完成後,我們可以得到任意單字在 “text” 的出現次數。

步驟 4

接下來,我們會宣告三個 “defaultdict”,分別為 “confidence”, “x_position”,以及 “candidates”。

“defaultdict” 是一個能賦予預設值的字典 (Dictionary )。宣告時,可以利用“lambda” 為每個鍵 ( Key ) 賦予預設的值 ( Value )。

例:若要宣告一個預設值皆為 0 的 “defaultdict”,可以輸入以下程式碼

example_dictionary = defaultdict(lambda: 0)

本次實作中,我們需要將 “confidence”, “x_position”, 以及“candidates” 三個字典 ( Dictionary ) 的預設值設為 0。

confidence, x_position, candidates = defaultdict(lambda: 0), defaultdict(lambda: 0), defaultdict(lambda: 0)

“confidence” 總共有 26 個鍵,分別為 26 個英文字母,各自記錄著它被 Pixetto 偵測到的信心程度。每當 Pixetto 偵測到某字母時,該字母在“confidence” 中的值便會上升。您可以將其理解為每個字母被偵測到的次數。經過多次偵測,“confidence” 中擁有最高值的幾個鍵 ( Key ),即為手寫單字中出現的字母。

“x_position” 記錄著各個字母在影像中的 X 座標。數值越小代表它位於單字的左方,越大則代表位於右方。

創建完 “defaultdict” 後,宣告一個包含所有字母的字串,以便之後對各個字母進行操作。

alphabets = 'abcdefghijklmnopqrstuvwxyz'

為了讓程式更容易理解,我們可以讓使用者提供單字的長度。

word_len = int(input('Input the length of the word: '))

步驟 5

接下來,宣告一個 Pixetto Class,並連接指定的 Pixetto 視覺感測器 ( COM3 )。

pix = Pixetto()
pix.open("COM3")

連接威盛 Pixetto 視覺感測器後,我們可以先嘗試讀取威盛 Pixetto 的偵測資料。

while True:
    if pix.is_detected() == True:
        print(pix.get_data_list())

pix.close()

利用 “get_data_list()” 讀取威盛 Pixetto 回傳資料。每一筆資料都由兩個數字以及一個陣列所組成。第一個數字代表威盛Pixetto 目前的功能代號 ( 手寫字母辨識 ),第二個數字代表 Pixetto 偵測到之字母數量。最後的陣列則是由每個偵測到的字母的詳細資料所組成,除了字母 ( label ),亦包含了威盛 Pixetto 的辨識信心程度 ( confidence )、字母寬度高度 ( w, h ),以及 XY 座標 ( x, y )。

步驟 6

現在我們可以接收並記錄威盛 Pixetto 的回傳資料。

首先以 “objs” 紀錄回傳的陣列。由於我們只需陣列中的資料,無需第一及第二個回傳數字,因此可以先以底線 “_” 帶過。

while True:
    if pix.is_detected() == True:
        _, _, objs = pix.get_data_list()

        for item in objs:
            letter = item['label'][1] # 取小寫字母
            x_pos = item['x']
            conf = item['confidence']

變數 “objs” 是一個記錄偵測到之字母的陣列。我們可以利用 “for” 迴圈對陣列中每個元素做處理。

在迴圈裏,宣告三個變數,分別記錄偵測到之字母 ( letter )、字母之 X 座標( x_pos )、以及威盛Pixetto 的信心程度 (conf)。

在字典 “confidence” 中,將偵測到的字母對應到的值 ( Value ) 依據信心程度加上一定比例的值。為了不讓數值過高,可以使用 “min” 函式,將其上限設為100。

在字典 “x_position” 中,將偵測到的字母 ( letter ) 對應到的值 ( Value ) 更新。

for item in objs:
    letter = item['label'][1]
    x_pos = item['x']
    conf = item['confidence']
    confidence[letter] += 10 * conf
    confidence[letter] = min(confidence[letter], 100)
    x_position[letter] = x_pos
    conf = item['confidence']

字典 “confidence” 的資料處理完後,請在 “for” 迴圈外,依據 “confidence”中的各個值 ( Value ) ,由大到小做排序,並由 “result_1” 紀錄前 “word_len”名 ( “word_len” 為單字字母個數 )。

result_1 = sorted(confidence.items(), key=lambda item: -
item[1])[0:word_len]

“result_1” 記錄了威盛 Pixetto 所偵測到的字母。但我們仍需依據各個字母的 X座標進行排列。

result_1 = sorted(confidence.items(), key=lambda item: -
item[1])[0:word_len]

result_2 = []
for item in result_1:
result_2.append([item[0], x_position[item[0]]])
result_2 = sorted(result_2, key=lambda item: item[1])

為了防止誤判的字母在字典 “condifence” 中的值居高不下,我們可以在每次讀取威盛 Pixetto 後,將所有字母的信心程度下降。

for i in alphabets:
    if confidence[i] >= 2:
        confidence[i] -= 2
    else:
        confidence[i] = 0

現在,我們可以對程式碼進行測試。

步驟 7

在這個步驟中,我們要讓程式在偵測到正確單字後停止。

首先,我們必須確認單字出現在單字庫中,並且長度必須等於使用者在前面步驟輸入的 “word_len”。

若是上述條件都符合,將該單字在字典 “candidates” 中的值加一。

if WORDS[word] != 0 and len(word) == word_len:
    candidates[word] += 1

當偵測到的單字在字典 “candidates” 中的值超過十,便暫停程式,顯示該單字於螢幕上,並且詢問使用者是否繼續偵測。

if WORDS[word] != 0 and len(word) == word_len:
    candidates[word] += 1
    if candidates[word] > 10:
        print('\nFinished detecting!')
        print('Word detected: ', word)
        correct_word = input('Continue detecting? (y/n)\n')

若是使用者輸入 “n”,則跳出 “while” 迴圈,結束程式。

若是輸入 “y”,則將 “candidates” , “confidence”, 以及 “x_position” 重製,並詢問下一個欲偵測單字的長度。

correct_word = input('Continue detecting? (y/n)\n')
if correct_word == 'n':
    break
elif correct_word == 'y':
     word_len = int(input('Input the length of the word:'))
     candidates.clear()
     confidence.clear()
     x_position.clear()

我們已經完成了所有的程式碼了。現在,您可以在白紙上寫下五個字母以下的英文單字,並試著讓威盛 Pixetto 視覺感測器辨識。

結束辨識後,別忘了關閉威盛 Pixetto。

pix.close()

恭喜您完成了!

祝您玩得愉快,別忘了分享自己的創作至社群並標註 #VIAPixetto!

分享貼文!

Share on linkedin
Share on twitter
Share on facebook

Leave a Reply