##1. はじめに
Lチカで始めるテスト自動化シリーズ第五弾です。
M5StackとPythonで受入テスト自動化の要素技術を試すでインタラクティブシェルで行っていたWebカメラの画像キャプチャや画像の切り出し、OCRをテストランナーに組み込んで自動化します。
これまでの記事はこちらをご覧ください。
- Lチカで始めるテスト自動化
- Lチカで始めるテスト自動化(2)テストスクリプトの保守性向上
- Lチカで始めるテスト自動化(3)オシロスコープの組込み
- Lチカで始めるテスト自動化(4)テストスクリプトの保守性向上(2)
##2. テストランナーの改修
- 以下のコマンドおよび関数を作成します。
|コマンド|引数|機能
|--------+--------+--------
|open_cam|カメラデバイスの番号|OpenCVのVideoCaptureを実行する
|close_cam|なし|OpenCVのVideoCaptureをreleaseする
|capture_cam|ファイル名|カメラ画像をキャプチャしファイルに保存する
|crop_img|入力ファイル名
切り出し位置(縦)
切り出し位置(横)
出力ファイル名|指定されたエリアを画像ファイルから切り出してファイルに保存する
|open_ocr|なし|PyOCRを利用可能にする
|exec_ocr|OCRにかける画像ファイル名|画像ファイルからUS ASCIIの文字列をOCRする
- 冗長なコードを改修に併せて削除しました。
#!/usr/bin/python3
#
# This software includes the work that is distributed in the Apache License 2.0
#
from time import sleep
import serial
import codecs
import csv
import sys
import visa
import cv2
from PIL import Image
import pyocr
import pyocr.builders
UNINITIALIZED = 0xdeadbeef
def open_cam(camera_number):
return cv2.VideoCapture(camera_number)
def close_cam(cam):
if cam != UNINITIALIZED:
cam.release()
def capture_cam(cam, filename):
if cam != UNINITIALIZED:
_, img = cam.read()
cv2.imwrite(filename, img)
return True
else:
print("CAM Not Ready.")
return False
def crop_img(filename_in, v, h, filename_out):
img = cv2.imread(filename_in, cv2.IMREAD_COLOR)
v0 = int(v.split(':')[0])
v1 = int(v.split(':')[1])
h0 = int(h.split(':')[0])
h1 = int(h.split(':')[1])
img2 = img[v0:v1, h0:h1]
cv2.imwrite(filename_out, img2)
return True
def open_ocr():
ocr = pyocr.get_available_tools()
if len(ocr) != 0:
ocr = ocr[0]
else:
ocr = UNINITIALIZED
print("OCR Not Ready.")
return ocr
def exec_ocr(ocr, filename):
try:
txt = ocr.image_to_string(
Image.open(filename),
lang = "eng",
builder = pyocr.builders.TextBuilder()
)
except:
print("OCR Not Ready.")
else:
return txt
def serial_write(h, string):
if h != UNINITIALIZED:
string = string + '\n'
string = str.encode(string)
h.write(string)
return True
else:
print("UART Not Initialized.")
return False
def open_dso():
rm = visa.ResourceManager()
resources = rm.list_resources()
#print(resources)
for resource in resources:
#print(resource)
try:
dso = rm.open_resource(resource)
except:
print(resource, "Not Found.")
else:
print(resource, "Detected.")
return dso
#Throw an error to caller if none succeed.
return dso
def main():
is_passed = True
val = str(UNINITIALIZED)
cam = UNINITIALIZED
ocr = UNINITIALIZED
dso = UNINITIALIZED
uart = UNINITIALIZED
with codecs.open('script.csv', 'r', 'utf-8') as file:
script = csv.reader(file, delimiter=',', lineterminator='\r\n', quotechar='"')
with codecs.open('result.csv', 'w', 'utf-8') as file:
result = csv.writer(file, delimiter=',', lineterminator='\r\n', quotechar='"')
for cmd in script:
print(cmd)
if "#" in cmd[0]:
pass
elif cmd[0] == "sleep":
sleep(float(cmd[1]))
elif cmd[0] == "open_cam":
cam = open_cam(int(cmd[1]))
elif cmd[0] == "close_cam":
close_cam(cam)
cam = UNINITIALIZED
elif cmd[0] == "capture_cam":
ret = capture_cam(cam, cmd[1])
if ret == False:
is_passed = False
elif cmd[0] == "crop_img":
crop_img(cmd[1], cmd[2], cmd[3], cmd[4])
elif cmd[0] == "open_ocr":
ocr = open_ocr()
if ocr == UNINITIALIZED:
is_passed = False
elif cmd[0] == "exec_ocr":
try:
val = exec_ocr(ocr, cmd[1])
except:
is_passed = False
else:
cmd.append(str(val))
elif cmd[0] == "open_dso":
try:
dso = open_dso()
except:
is_passed = False
elif cmd[0] == "dso":
try:
if "?" in cmd[1]:
val = dso.query(cmd[1]).rstrip().replace(",", "-")
cmd.append(val)
else:
dso.write(cmd[1])
except:
is_passed = False
elif cmd[0] == "open_uart":
try:
uart = serial.Serial(cmd[1], 115200, timeout=1.0, dsrdtr=1)
except:
is_passed = False
elif cmd[0] == "send":
ret = serial_write(uart, cmd[1])
if ret == False:
is_passed = False
elif cmd[0] == "rcvd":
try:
val = uart.readline().strip().decode('utf-8')
cmd.append(val)
except:
is_passed = False
elif cmd[0] == "eval_str_eq":
if str(val) != str(cmd[1]):
is_passed = False
elif cmd[0] == "eval_int_eq":
if int(val) != int(cmd[1]):
is_passed = False
elif cmd[0] == "eval_int_gt":
if int(val) < int(cmd[1]):
is_passed = False
elif cmd[0] == "eval_int_lt":
if int(val) > int(cmd[1]):
is_passed = False
elif cmd[0] == "eval_dbl_eq":
if float(val) != float(cmd[1]):
is_passed = False
elif cmd[0] == "eval_dbl_gt":
if float(val) < float(cmd[1]):
is_passed = False
elif cmd[0] == "eval_dbl_lt":
if float(val) > float(cmd[1]):
is_passed = False
else:
cmd.append("#")
if is_passed == True:
cmd.append("OK")
print(cmd)
result.writerow(cmd)
else:
cmd.append("NG")
print(cmd)
result.writerow(cmd)
close_cam(cam)
print("FAIL")
sys.exit(1)
if is_passed == True:
close_cam(cam)
print("PASS")
sys.exit(0)
main()
##3. テストスクリプト
crop_imgコマンドの確認はM5StackとPythonで受入テスト自動化の要素技術を試すの3.2 Webcam画像のキャプチャで生成された123abc.bmpファイルを使用しています。
open_cam,1
capture_cam,abc1234.jpg
close_cam
crop_img,123abc.bmp,234:274,240:400,123abc_crop.bmp
open_ocr
exec_ocr,123abc_crop.bmp
eval_str_eq,123-abc
##4. テスト実行結果
- 実行環境はWindows10(1909)+Anaconda3 2019.07 (Python 3.6.9 64-bit)です1。
- Webカメラの画像を保存できました。
- crop_imgコマンドで切り出した画像をOCRにかけて、期待値の文字列と一致することを自動で確認できました。
open_cam,1,OK
capture_cam,abc1234.jpg,OK
close_cam,OK
crop_img,123abc.bmp,234:274,240:400,123abc_crop.bmp,OK
open_ocr,OK
exec_ocr,123abc_crop.bmp,123-abc,OK
eval_str_eq,123-abc,OK
##5. おわりに
UARTのコマンド送受信、組込み機器のスイッチのON/OFF、ArduinoのA/Dコンバータを使った電圧測定、オシロスコープの組込み、Webカメラの組込み、画像の切り出し、OCRを使った文字列の抽出と比較をできるようになりました。組込み機器やIoTデバイスのシステムレベルのCI/CDの可能性がだいぶ上がったように思います。
-
OpenCV、Tesseract-OCR、PyOCR、PILのインストールやTesseract-OCRの環境変数の設定はM5StackとPythonで受入テスト自動化の要素技術を試すをご覧ください。 ↩