Edited at

M5StackとPythonで受入テスト自動化の要素技術を試す


1. はじめに

ArduinoとオシロスコープをPythonで制御して測定するの続編です。前回は性能テスト自動化の基礎となるオシロスコープの制御をPyVISAで行いましたが、今回は受入テスト自動化を想定しPySerialでIoTデバイスを制御するとともにデバイスの画面をWebcamで撮影してOpenCVとPyOCRで画像から文字列を抽出します。

OpenCVとPythonによる受入テスト自動化はJaSST'17 Tokyoの「受け入れテストの自動化 ~ OpenCVの「眼」で捉え、Pythonの「脳」が思考し、Appiumの「指」で動かす」(講演資料:受け入れテストの自動化)からヒントを得ています。なお、この講演資料は自動化だけでなく期待結果のデータの作り方やデータの構造などさまざまな工夫が紹介されていて参考になります。


2. 構成要素


2.1 ハードウェア


  • M5Stack



    • M5Stackは5センチ角のスタッカブル(積み重ね可能)なIoTデバイスのプロトタイプモジュールです。

    • CPUにESP32を、ペリフェラルとしてWiFi&Bluetooth(技適取得済み)、2インチ(320x240)のTFT液晶、3個のボタン、スピーカー、GROVE端子(I2C)、UARTやバッテリーなどを搭載し、C/C++やMicroPython、Scratchでプログラムを開発できます。

    • 9軸IMU(3軸加速度、3軸ジャイロ、3軸コンパスセンサ)やNeoPixel(RGB LED)、マイクを備える上位機種やプロトタイピングを加速するさまざまなオプションが用意されています(AliExpressスイッチサイエンス(国内代理店))。

    • M5StackはIoTデバイスのプロトタイプモジュールですが今回はこれをソフトウェアテストのツール開発のプロトタイプとして使用し、UARTからコマンドの引数で受け取った文字列を液晶画面に表示する機能をM5Stackに実装しました。詳細はM5Stackでスマートウォッチをご参照ください。




  • Logicool HD Webcam C270


    • 接写できるようにピント調整しています。改造すると保証が効かなくなりますので試す際は自己責任でお願いします。




2.2 ソフトウェア

筆者はMinicondaでPythonの実行環境を用意しています。condaとpipは混ぜるな危険1という話がありますのでcondaコマンドでインストールする方法を以下に示します。


  • OpenCV

  • Tesseract-OCR

  • PyOCR

  • PIL


2.2.1 OpenCVのインストール

conda install -c conda-forge opencv

コマンド実行ログ

>conda install -c conda-forge opencv

Solving environment: done

## Package Plan ##

environment location: C:\Users\hoge\Miniconda3

added / updated specs:
- opencv

The following packages will be downloaded:

package | build
---------------------------|-----------------
win_inet_pton-1.0.1 | py36_1002 5 KB conda-forge
urllib3-1.23 | py36_1001 152 KB conda-forge
certifi-2018.10.15 | py36_1000 138 KB conda-forge
icu-58.2 | vc14_0 21.8 MB conda-forge
qt-5.6.2 | vc14_1 55.6 MB conda-forge
pycosat-0.6.3 |py36hfa6e2cd_1001 97 KB conda-forge
requests-2.20.1 | py36_1000 84 KB conda-forge
ruamel_yaml-0.15.71 |py36hfa6e2cd_1000 271 KB conda-forge
wincertstore-0.2 | py36_1002 13 KB conda-forge
conda-4.5.11 | py36_1000 655 KB conda-forge
blas-1.0 | mkl 6 KB
mkl-2019.0 | 118 178.1 MB
wheel-0.32.2 | py36_0 51 KB conda-forge
mkl_random-1.0.2 | py36_0 267 KB conda-forge
chardet-3.0.4 | py36_1003 209 KB conda-forge
intel-openmp-2019.0 | 118 1.7 MB
python-3.6.6 | he025d50_0 21.4 MB conda-forge
jpeg-9c | hfa6e2cd_1001 314 KB conda-forge
libpng-1.6.34 | vc14_0 547 KB conda-forge
numpy-base-1.15.4 | py36h8128ebf_0 3.9 MB
libwebp-0.5.2 | vc14_7 1.1 MB conda-forge
zlib-1.2.11 | vc14_0 119 KB conda-forge
pysocks-1.6.8 | py36_1002 22 KB conda-forge
six-1.11.0 | py36_1001 20 KB conda-forge
cryptography-2.3.1 |py36h74b6da3_1000 506 KB conda-forge
cffi-1.11.5 |py36hfa6e2cd_1001 211 KB conda-forge
pyopenssl-18.0.0 | py36_1000 80 KB conda-forge
icc_rt-2017.0.4 | h97af966_0 8.0 MB
setuptools-40.6.2 | py36_0 585 KB conda-forge
pywin32-224 |py36hfa6e2cd_1000 6.8 MB conda-forge
pyvisa-1.9.1 | py36_1000 232 KB conda-forge
mkl_fft-1.0.6 | py36_0 121 KB conda-forge
pycparser-2.19 | py_0 87 KB conda-forge
libtiff-4.0.9 | vc14_0 616 KB conda-forge
asn1crypto-0.24.0 | py36_1003 154 KB conda-forge
numpy-1.15.4 | py36ha559c80_0 36 KB
idna-2.7 | py36_1002 131 KB conda-forge
menuinst-1.4.14 | py36_1000 93 KB conda-forge
pip-18.1 | py36_1000 1.8 MB conda-forge
opencv-3.4.3 | py36h597e314_201 54.0 MB conda-forge
------------------------------------------------------------
Total: 359.8 MB

The following NEW packages will be INSTALLED:

blas: 1.0-mkl
icc_rt: 2017.0.4-h97af966_0
icu: 58.2-vc14_0 conda-forge [vc14]
intel-openmp: 2019.0-118
jpeg: 9c-hfa6e2cd_1001 conda-forge
libpng: 1.6.34-vc14_0 conda-forge [vc14]
libtiff: 4.0.9-vc14_0 conda-forge [vc14]
libwebp: 0.5.2-vc14_7 conda-forge [vc14]
mkl: 2019.0-118
mkl_fft: 1.0.6-py36_0 conda-forge
mkl_random: 1.0.2-py36_0 conda-forge
numpy: 1.15.4-py36ha559c80_0
numpy-base: 1.15.4-py36h8128ebf_0
opencv: 3.4.3-py36h597e314_201 conda-forge
qt: 5.6.2-vc14_1 conda-forge [vc14]
zlib: 1.2.11-vc14_0 conda-forge [vc14]

The following packages will be UPDATED:

asn1crypto: 0.24.0-py37_0 --> 0.24.0-py36_1003 conda-forge
certifi: 2018.10.15-py37_1000 conda-forge --> 2018.10.15-py36_1000 conda-forge
cffi: 1.11.5-py37h74b6da3_1 --> 1.11.5-py36hfa6e2cd_1001 conda-forge
chardet: 3.0.4-py37_1 --> 3.0.4-py36_1003 conda-forge
conda: 4.5.11-py37_1000 conda-forge --> 4.5.11-py36_1000 conda-forge
cryptography: 2.3.1-py37h74b6da3_0 --> 2.3.1-py36h74b6da3_1000 conda-forge
idna: 2.7-py37_0 --> 2.7-py36_1002 conda-forge
menuinst: 1.4.14-py37hfa6e2cd_0 --> 1.4.14-py36_1000 conda-forge
pip: 10.0.1-py37_0 --> 18.1-py36_1000 conda-forge
pycosat: 0.6.3-py37hfa6e2cd_0 --> 0.6.3-py36hfa6e2cd_1001 conda-forge
pycparser: 2.18-py37_1 --> 2.19-py_0 conda-forge
pyopenssl: 18.0.0-py37_0 --> 18.0.0-py36_1000 conda-forge
pyserial: 3.4-py_2 conda-forge --> 3.4-py_2 conda-forge
pysocks: 1.6.8-py37_0 --> 1.6.8-py36_1002 conda-forge
pyvisa: 1.9.1-py37_1000 conda-forge --> 1.9.1-py36_1000 conda-forge
pywin32: 223-py37hfa6e2cd_1 --> 224-py36hfa6e2cd_1000 conda-forge
requests: 2.19.1-py37_0 --> 2.20.1-py36_1000 conda-forge
ruamel_yaml: 0.15.46-py37hfa6e2cd_0 --> 0.15.71-py36hfa6e2cd_1000 conda-forge
setuptools: 40.2.0-py37_0 --> 40.6.2-py36_0 conda-forge
six: 1.11.0-py37_1 --> 1.11.0-py36_1001 conda-forge
urllib3: 1.23-py37_0 --> 1.23-py36_1001 conda-forge
wheel: 0.31.1-py37_0 --> 0.32.2-py36_0 conda-forge
win_inet_pton: 1.0.1-py37_1 --> 1.0.1-py36_1002 conda-forge
wincertstore: 0.2-py37_0 --> 0.2-py36_1002 conda-forge

The following packages will be DOWNGRADED:

python: 3.7.0-hea74fb7_0 --> 3.6.6-he025d50_0 conda-forge

Proceed ([y]/n)? y

Downloading and Extracting Packages
win_inet_pton-1.0.1 | 5 KB | ###################################################################################### | 100%
urllib3-1.23 | 152 KB | ###################################################################################### | 100%
certifi-2018.10.15 | 138 KB | ###################################################################################### | 100%
icu-58.2 | 21.8 MB | ###################################################################################### | 100%
qt-5.6.2 | 55.6 MB | ###################################################################################### | 100%
pycosat-0.6.3 | 97 KB | ###################################################################################### | 100%
requests-2.20.1 | 84 KB | ###################################################################################### | 100%
ruamel_yaml-0.15.71 | 271 KB | ###################################################################################### | 100%
wincertstore-0.2 | 13 KB | ###################################################################################### | 100%
conda-4.5.11 | 655 KB | ###################################################################################### | 100%
blas-1.0 | 6 KB | ###################################################################################### | 100%
mkl-2019.0 | 178.1 MB | ###################################################################################### | 100%
wheel-0.32.2 | 51 KB | ###################################################################################### | 100%
mkl_random-1.0.2 | 267 KB | ###################################################################################### | 100%
chardet-3.0.4 | 209 KB | ###################################################################################### | 100%
intel-openmp-2019.0 | 1.7 MB | ###################################################################################### | 100%
python-3.6.6 | 21.4 MB | ###################################################################################### | 100%
jpeg-9c | 314 KB | ###################################################################################### | 100%
libpng-1.6.34 | 547 KB | ###################################################################################### | 100%
numpy-base-1.15.4 | 3.9 MB | ###################################################################################### | 100%
libwebp-0.5.2 | 1.1 MB | ###################################################################################### | 100%
zlib-1.2.11 | 119 KB | ###################################################################################### | 100%
pysocks-1.6.8 | 22 KB | ###################################################################################### | 100%
six-1.11.0 | 20 KB | ###################################################################################### | 100%
cryptography-2.3.1 | 506 KB | ###################################################################################### | 100%
cffi-1.11.5 | 211 KB | ###################################################################################### | 100%
pyopenssl-18.0.0 | 80 KB | ###################################################################################### | 100%
icc_rt-2017.0.4 | 8.0 MB | ###################################################################################### | 100%
setuptools-40.6.2 | 585 KB | ###################################################################################### | 100%
pywin32-224 | 6.8 MB | ###################################################################################### | 100%
pyvisa-1.9.1 | 232 KB | ###################################################################################### | 100%
mkl_fft-1.0.6 | 121 KB | ###################################################################################### | 100%
pycparser-2.19 | 87 KB | ###################################################################################### | 100%
libtiff-4.0.9 | 616 KB | ###################################################################################### | 100%
asn1crypto-0.24.0 | 154 KB | ###################################################################################### | 100%
numpy-1.15.4 | 36 KB | ###################################################################################### | 100%
idna-2.7 | 131 KB | ###################################################################################### | 100%
menuinst-1.4.14 | 93 KB | ###################################################################################### | 100%
pip-18.1 | 1.8 MB | ###################################################################################### | 100%
opencv-3.4.3 | 54.0 MB | ###################################################################################### | 100%
Preparing transaction: done
Verifying transaction: done
Executing transaction: done



2.2.2 Tesseract-OCRのインストール


2.2.2.1 Tesseract-OCRのダウンロードとインストール

https://github.com/UB-Mannheim/tesseract/wikiからtesseract-ocr-setup-3.05.02-20180621.exeをダウンロードし自分が使いそうな言語にチェックを入れてインストールします。

参考:(2017年12月) PythonとOpenCVをこれからやってみる - 3 - 文字認識(1)

補足:

最新版のtesseract-ocr-w64-setup-v4.0.0.20181030.exeはpythonから呼び出した際に以下のようなエラーダイアログが表示されたためtesseract-ocr-setup-3.05.02-20180621.exeを使用しています。


2.2.2.2 Tesseract-OCRの環境変数の設定

PATHとTESSDATA_PREFIXを設定します。

>set path=%PATH%;C:\Program Files (x86)\Tesseract-OCR

>set TESSDATA_PREFIX=C:\Program Files (x86)\Tesseract-OCR

バージョンとインストールした言語を確認します。

>tesseract -v

tesseract 3.05.02
leptonica-1.75.3
libgif 5.1.4 : libjpeg 8d (libjpeg-turbo 1.5.3) : libpng 1.6.34 : libtiff 4.0.9 : zlib 1.2.11 : libwebp 0.6.1 : libopenjp2 2.2.0

>tesseract --list-langs
List of available languages (5):
chi_sim
chi_tra
eng
jpn
osd

参考:pipを使わずwindows10にtesseractとPyOCRをインストールする方法


2.2.3 PyOCRのインストール

https://anaconda.org/brianjmcguirk/pyocr/filesからpyocr-0.5-py36h6a457c8_0.tar.bz2をダウンロードしcondaコマンドでインストールします。

conda install c:\Users\hoge\Downloads\pyocr-0.5-py36h6a457c8_0.tar.bz2

コマンド実行ログ

>conda install c:\Users\hoge\Downloads\pyocr-0.5-py36h6a457c8_0.tar.bz2

Downloading and Extracting Packages
######################################################################################################################### | 100%
Preparing transaction: done
Verifying transaction: done
Executing transaction: done


続いて、tesseract.pyとbuilders.pyの"-psm"を"--psm"に修正します。

参考:pipを使わずwindows10にtesseractとPyOCRをインストールする方法

参考:pyocrでtesseractを動かせなかった問題


2.2.4 PILのインストール

画像の読み込みに使用するPILライブラリをインストールします。

conda install -c conda-forge pillow

コマンド実行ログ

>conda install -c conda-forge pillow

Solving environment: done

## Package Plan ##

environment location: C:\Users\hoge\Miniconda3

added / updated specs:
- pillow

The following packages will be downloaded:

package | build
---------------------------|-----------------
tk-8.6.8 | vc14_0 3.8 MB conda-forge
olefile-0.46 | py_0 31 KB conda-forge
freetype-2.9.1 | he8b6a0d_1004 470 KB conda-forge
pillow-5.3.0 |py36h9a613e6_1000 778 KB conda-forge
------------------------------------------------------------
Total: 5.0 MB

The following NEW packages will be INSTALLED:

freetype: 2.9.1-he8b6a0d_1004 conda-forge
olefile: 0.46-py_0 conda-forge
pillow: 5.3.0-py36h9a613e6_1000 conda-forge
tk: 8.6.8-vc14_0 conda-forge [vc14]

Proceed ([y]/n)? y

Downloading and Extracting Packages
tk-8.6.8 | 3.8 MB | ###################################################################################### | 100%
olefile-0.46 | 31 KB | ###################################################################################### | 100%
freetype-2.9.1 | 470 KB | ###################################################################################### | 100%
pillow-5.3.0 | 778 KB | ###################################################################################### | 100%
Preparing transaction: done
Verifying transaction: done
Executing transaction: done



3. 自動化の要素技術を試す


3.1 M5Stackの画面への文字列描画

M5Stackへ自作の以下のコマンドをPySerialで送信します。


  1. serialをimportし通信設定を行う

  2. "x" を送信しコマンドモードへ遷移する(xは任意の文字)

  3. m5lcd clear (LCDを全面黒で描画する)

  4. m5lcd str 123-abc (123-abcという文字列を画面の中心に描画する)

>python

Python 3.6.6 | packaged by conda-forge | (default, Jul 26 2018, 11:48:23) [MSC v.1900 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>>
>>>
>>> import serial
>>> ser = serial.Serial("COM7", 115200)
>>>
>>>
>>> data = "x"
>>> data = data + '\r\n'
>>> ser.write(bytes(data, 'UTF-8'))
3
>>> data = "m5lcd clear"
>>> data = data + '\r\n'
>>> ser.write(bytes(data, 'UTF-8'))
13
>>> data = "m5lcd str 123-abc"
>>> data = data + '\r\n'
>>> ser.write(bytes(data, 'UTF-8'))
19
>>>


3.2 Webcam画像のキャプチャ

Webcamの画像をOpenCVを使用して表示するとともに任意のファイル名(例:123abc.bmp)で画像を保存します。

キャプチャした画像を以下に示します。カメラがアクティブになると点灯する緑のLEDは写り込まないようテープで目隠しすると良いと思いました。

参考:ゼロからはじめるPython(35) OpenCVで監視カメラを自作してみよう | マイナビニュース camera.py


3.3 文字列エリアの切り出し

import cv2

filename = "123abc.bmp"

img = cv2.imread(filename, cv2.IMREAD_COLOR)
img2 = img[236:274, 240:400]

cv2.imshow('window title', img2)
cv2.waitKey(0)

任意のファイル名(例:123abc_core.png)で画像を保存します。

参考:(2017年12月) PythonとOpenCVをこれからやってみる - 1 - はじめの一歩


3.4 OCR

from PIL import Image

import sys

import pyocr
import pyocr.builders

tools = pyocr.get_available_tools()
if len(tools) == 0:
print("No OCR tool found")
sys.exit(1)

tool = tools[0]

txt = tool.image_to_string(
Image.open('123abc_core.png'),
lang='eng',
builder=pyocr.builders.TextBuilder()
)
print(txt)

スクリプトの実行結果を以下に示します。画面に表示した文字列を抽出できました。

>python ocr.py

123-abc

なお、「2.2.2.2 Tesseract-OCRの環境変数の設定」がされていないと

No OCR tool found

というメッセージが返ってきます。

参考:pyocr README.markdown

参考:Pythonで画像からテキストを抽出する


4. おわりに


  • Pythonを自動化システムのハブとすることでテスト対象デバイスの制御、測定器の制御、カメラ画像の取得や文字列の抽出を統合的に扱うことができました。WebやスマホアプリであればSeleniumやAppiumといった自動化のインフラが整っていますが組込みやIoTデバイスも自動化の第一歩の敷居は思ったほど高くないかもと思いました。

  • IoTデバイスのプログラムをMicroPythonで作れたりテストベンチをPythonで書けるのはライブラリが充実していることもあってすごいなあ便利だなあと思いました。また、アセンブラやCを知らなくてもPythonを足掛かりにして組込みに参入できる(かもしれない)と考えると、Pythonでプログラムを書けるとできることが広がると思いました。

  • 自動化プラットホームとしてExcel23とPythonを触ってみて適材適所と思いました。測定器でN回測定して最大値や最小値、平均値、中央値などを計算してグラフにしたりGo/No-GO判定するならExcelが便利だし受入テストであればPythonが便利と思いました。

  • 最後に、筆者はPythonのプログラムはほとんど書いておらず参考にさせていただいた記事のプログラムをコピペしてちょっと直した程度です。記事を公開されている皆様に感謝いたします。