生産現場IoTへの挑戦 #08　～Raspberry PiとUSBカメラで外観検査装置を作る　前編～

# 1.はじめに

今回のタスクはRaspberry PiとUSBカメラを使って画像処理による外観検査装置を作ることです。
いわゆる外観検査装置には様々な専門メーカーが非常に多機能な装置を提供しており入手も簡単ですが、高価すぎてコストメリットが出しにくいケースもあるかと思います。
ちなみにみんな大好き[○ーエンス](https://www.keyence.co.jp/products/vision/vision-sensor/cv-x100/)さんの外観検査装置は、カメラ＋コントローラー＋照明でざっくり150万円くらいしますが、今回は照明無し、カメラ（USBカメラ）＋コントローラー（Raspberry Pi 4B 4GB）の計1万円強でやってみます。

外観検査をする際は撮影環境の設定がとても重要です。
**前編ではv4l2によるカメラのパラメータ設定を行います。**
[後編](https://qiita.com/airpocket/items/760d273e3fb2d53bf321)では実際に検査を行うプログラムを解説します。

# 2.検査する内容

今回トライするのは、「樹脂成型部品のショートショットの検出」です。ショートショットと言うのは、樹脂の射出成型を行う際に樹脂の量や圧力不足により金型の端の方まで樹脂が流れ切らず少し欠けてしまう現象です。
汎用的に言い換えると「製品外縁部の欠けの検出」と言った感じでしょうか。

使用したワークはサーボモーターに使用するサーボホーンと言う部品で、下の写真の様な形状です。全長35mmほどの小さな成型品ですが、上下二個のうち下の部品は右端が少し欠けています。今回はこの欠けの有無を検出してみます。
<img width="400￥" alt="_20220303_094456.JPG" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/496179/d6806402-54b2-fefc-be7d-b53867afb9f6.jpeg">

# 3.使用する機材
使用する機材は次の通りです。

### 3-1.Raspberry Pi
RPiは4Bの4GB版を使用します。入手しにくいので困ります。
OSはBullseyeの64bit版を使用しています。

```sh
pi@raspberrypi:~ $ lsb_release -a
No LSB modules are available.
Distributor ID:	Debian
Description:	Debian GNU/Linux 11 (bullseye)
Release:	11
Codename:	bullseye
pi@raspberrypi:~ $ uname -a
Linux raspberrypi 5.10.92-v8+ #1514 SMP PREEMPT Mon Jan 17 17:39:38 GMT 2022 aarch64 GNU/Linux
```


### 3-2.USBカメラ
カメラはAliexpressで購入した[AUSDOMのAW615S](https://www.aliexpress.com/item/1005002297615839.html?spm=a2g0o.order_list.0.0.21ef1802rjBFp1)というカメラを使用します。アマゾンだと少し割高かつクランプ形状が異なりますが、たぶん[これ](https://www.amazon.co.jp/dp/B017SRHRR6/ref=cm_sw_r_tw_dp_RKAP4P120D2T3TQRZMFX)が同じやつですね。

<img width="400￥" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/496179/c0d94607-77e4-21d6-de74-8590c598102a.png">

必要とするスペックは1280×720以上の解像度と~~マニュアルフォーカスであること~~。

~~**マニュアルフォーカスであること。**~~

~~重要なので2度言いました<font color="red">（オートフォーカスカメラもマニュアル制御できるっぽいので調べて追記します）</font>。解像度は検出したい欠陥のサイズにもよりますので必ずしも必須ではありませんが、マニュアルフォーカスは必須です。オートフォーカスカメラでは、ピンボケの可能性が高くなりますので必ずマニュアルフォーカスのカメラを使用してください。~~

<font color="red">**v4l2を使えば、オートフォーカスカメラでもフォーカスをマニュアル制御できることが確認できました。[確認結果はこちらにまとめています。](https://qiita.com/airpocket/items/74f87cb22505d70d424a)**</font>

[1万円クラスのUSBカメラ](https://www.amazon.co.jp/dp/B08FDH621H/ref=cm_sw_r_tw_dp_HMH9HM850H49H55Y916K?_encoding=UTF8&psc=1)（レンズ交換できるこのようなカメラもとても便利です。私も試験用に一台持っています）であればマニュアルフォーカスレンズを使えるものもありますが、安価なウェブカメラでマニュアルフォーカスとなると選択肢が限られます。なおかつ、このカメラはレンズからの距離が10mmくらいでもピントを合わせられてマクロレンズ的な使い方もできてしまう優れものです。（さすがに画像周縁部はボケます）
Aliexpressで送料込み約3000円と非常に安価でミニ三脚もついていますので一台持っていても損はしないと思います。

# 4.カメラの設定について
今回はマニュアルフォーカスカメラを使用しますが、これはオートフォーカスではピントの調節がカメラ任せになってしまい、期待する画像を得ることができないためです。
カメラの内部ではフォーカス以外にもいくつかのパラメータが自動的に制御されています。具体的には撮影時の露出やゲイン、画像出力時の明るさやホワイトバランスなどです。周囲の環境光が変化する場合、これらのパラメータを自動的に制御してくれると便利なのですが意図しない結果を生む要因ともなりますので、画像処理を行う場合は環境光を一定に保ちカメラパラメータを手動で制御するのがセオリーです。
前編ではv4l2というツールを使ってカメラのパラメータ制御をします。

※画像処理のアルゴリズムのロバスト性を高くすると、自動でパラメータを制御した方が便利な場合もあります。

# 5.v4l2を使う
v4l2とは、Video for linux 2の略で、Linux上でリアルタイムビデオキャプチャを行うための便利なツールです。カメラを制御するためのたくさんのAPIも含まれていますのでこれを使います。

## 5.1 インストール
まずはインストールです。v4l-utilsとopencv-pythonをインストールしておきます。

```sh
sudo apt update
sudo apt upgrade
sudo apt install v4l-utils
pip install opencv-python
```


##  5.2 カメラの動作確認
インストールができたら、USBカメラをRaspberry Piに接続し確認していきます。
まずはlsusbコマンドでUSBカメラが認識されているか見てみます。

```sh
pi@raspberrypi:~ $ lsusb
Bus 002 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub
Bus 001 Device 017: ID 1bcf:2284 Sunplus Innovation Technology Inc. Full HD webcam
Bus 001 Device 002: ID 2109:3431 VIA Labs, Inc. Hub
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
```

Sunplus Innovation Technology Inc. Full HD webcamと言う名前でカメラが認識されていました。
/devフォルダを確認してみます。

```sh
pi@raspberrypi:~ $ ls -ll /dev/video*
crw-rw----+ 1 root video 81, 12  3月  3 10:59 /dev/video0
crw-rw----+ 1 root video 81, 13  3月  3 10:59 /dev/video1
crw-rw----+ 1 root video 81,  3  2月 21 14:01 /dev/video10
crw-rw----+ 1 root video 81,  6  2月 21 14:01 /dev/video11
crw-rw----+ 1 root video 81, 10  2月 21 14:01 /dev/video12
crw-rw----+ 1 root video 81,  0  2月 21 14:01 /dev/video13
crw-rw----+ 1 root video 81,  1  2月 21 14:01 /dev/video14
crw-rw----+ 1 root video 81,  2  2月 21 14:01 /dev/video15
crw-rw----+ 1 root video 81,  4  2月 21 14:01 /dev/video16
crw-rw----+ 1 root video 81, 11  2月 21 14:01 /dev/video18
crw-rw----+ 1 root video 81,  5  2月 21 14:01 /dev/video20
crw-rw----+ 1 root video 81,  7  2月 21 14:01 /dev/video21
crw-rw----+ 1 root video 81,  8  2月 21 14:01 /dev/video22
crw-rw----+ 1 root video 81,  9  2月 21 14:01 /dev/video23
```

/dev/video0と/dev/video1の二つのデバイスファイルができていますが、ビデオ入力されているのはvideo0です。

次のコードでカメラが動く確認してみましょう。

```python
import cv2

capture = cv2.VideoCapture(0)

if capture.isOpened() is False:
    raise IOError

while(True):
    ret, frame = capture.read()
    if ret is False:
        raise IOError
    cv2.imshow('frame',frame)
    key = cv2.waitKey(1)
    if key == 27: #ESC
        break

capture.release()
cv2.destroyAllWindows()
```

# 5.3 v4l2でカメラ情報を確認

続いてv4l2を使って、カメラの情報を見てみます。

まずはカメラで使用できる解像度を確認します。

```sh
pi@raspberrypi:~ $ v4l2-ctl --list-formats-ext
ioctl: VIDIOC_ENUM_FMT
	Type: Video Capture

	[0]: 'MJPG' (Motion-JPEG, compressed)
		Size: Discrete 640x480
			Interval: Discrete 0.033s (30.000 fps)
		Size: Discrete 1920x1080
			Interval: Discrete 0.033s (30.000 fps)
		Size: Discrete 320x240
			Interval: Discrete 0.033s (30.000 fps)
		Size: Discrete 800x600
			Interval: Discrete 0.033s (30.000 fps)
		Size: Discrete 1280x720
			Interval: Discrete 0.033s (30.000 fps)
		Size: Discrete 1024x576
			Interval: Discrete 0.033s (30.000 fps)
	[1]: 'YUYV' (YUYV 4:2:2)
		Size: Discrete 640x480
			Interval: Discrete 0.033s (30.000 fps)
		Size: Discrete 1920x1080
			Interval: Discrete 0.200s (5.000 fps)
		Size: Discrete 320x240
			Interval: Discrete 0.033s (30.000 fps)
		Size: Discrete 800x600
			Interval: Discrete 0.050s (20.000 fps)
		Size: Discrete 1280x720
			Interval: Discrete 0.100s (10.000 fps)
		Size: Discrete 1024x576
			Interval: Discrete 0.067s (15.000 fps)
```

解像度は最小320×240から最大1920×1080の6種類のフォーマットに対応していました。

カメラの全情報を表示するコマンドは
v4l2-ctl --all
ですが長くなるので割愛します。
v4l2-ctl -L コマンドで変更可能なパラメータだけを抽出して表示してみます。

```sh
pi@raspberrypi:~ $ v4l2-ctl -L
                     brightness 0x00980900 (int)    : min=0 max=255 step=1 default=130 value=130
                       contrast 0x00980901 (int)    : min=0 max=255 step=1 default=120 value=120
                     saturation 0x00980902 (int)    : min=0 max=255 step=1 default=128 value=128
 white_balance_temperature_auto 0x0098090c (bool)   : default=1 value=1
                          gamma 0x00980910 (int)    : min=0 max=255 step=1 default=150 value=150
                           gain 0x00980913 (int)    : min=0 max=255 step=1 default=0 value=0
           power_line_frequency 0x00980918 (menu)   : min=0 max=2 default=1 value=1
				0: Disabled
				1: 50 Hz
				2: 60 Hz
      white_balance_temperature 0x0098091a (int)    : min=2800 max=6500 step=1 default=4600 value=4600 flags=inactive
                      sharpness 0x0098091b (int)    : min=0 max=255 step=1 default=150 value=150
         backlight_compensation 0x0098091c (int)    : min=0 max=2 step=1 default=1 value=1
                  exposure_auto 0x009a0901 (menu)   : min=0 max=3 default=3 value=3
				1: Manual Mode
				3: Aperture Priority Mode
              exposure_absolute 0x009a0902 (int)    : min=3 max=2047 step=1 default=166 value=166 flags=inactive
                   pan_absolute 0x009a0908 (int)    : min=-36000 max=36000 step=3600 default=0 value=0
                  tilt_absolute 0x009a0909 (int)    : min=-36000 max=36000 step=3600 default=0 value=0
                  zoom_absolute 0x009a090d (int)    : min=0 max=60 step=1 default=0 value=0
```

このカメラでは、撮影時の exposure_auto（露出）と画像出力時の white_balance_temperature_auto（ホワイトバランス）の自動制御をしていることが判りました。

## 5.4 露出のマニュアルコントロール
では、v4l2で露出をマニュアルコントロールしてみます。
変化が判る様、5.2項で実行したカメラの動作確認プログラムでリアルタイムキャプチャ画像を表示しながら画像の変化を見てみます。

デフォルトではexpsure_autoが'3'となっていますのでこれを'1'に変更し、exposure_absoluteの値を最小の'3'にしてみます。

```sh
v4l2-ctl -d /dev/video0 -c exposure_auto=1 -c exposure_absolute=3
```

画面が真っ暗になったら正しく露出を手動制御できています。
露出を最大値の2047にしてみると画面が明るくなりすぎたと思います。

```sh
v4l2-ctl -d /dev/video0 -c exposure_auto=1 -c exposure_absolute=2047
```

周りの明るさにもよりますが、室内の場合300~500程度にして固定します。

## 5.5　ホワイトバランスのマニュアルコントロール

続いてホワイトバランスもマニュアルにします。
ホワイトバランスは露出程分かりやすくはありませんが、露出固定の状態でカメラ前面に真っ赤な布などを映すと色味が変化するのが判ります。赤い布を映した直後は真っ赤に見えるのに、すぐに色あせた様に変化します。これはホワイトバランスを自動調節しているためです。
ホワイトバランスを手動制御にし、値をデフォルトの4600にします。

```shell
v4l2-ctl -d /dev/video0 -c white_balance_temperature_auto=0
v4l2-ctl -d /dev/video0 -c white_balance_temperature=4600
```

最後にもう一度カメラパラメータを確認してみます。

変更した部分のみ確認すると、、、

```
 white_balance_temperature_auto 0x0098090c (bool)   : default=1 value=0
      white_balance_temperature 0x0098091a (int)    : min=2800 max=6500 step=1 default=4600 value=4600
                  exposure_auto 0x009a0901 (menu)   : min=0 max=3 default=3 value=1
              exposure_absolute 0x009a0902 (int)    : min=3 max=2047 step=1 default=166 value=300
```

それぞれマニュアル動作になり、指定した値に設定されています。

# 6.まとめ

前編ではopencvを使った外観検査の導入編としてv4l2を使ったUSBカメラの設定について紹介しました。[後編](https://qiita.com/airpocket/items/760d273e3fb2d53bf321)ではopencvを使った形状解析について紹介したいと思います。

引合いに出した[○ーエンス](https://www.keyence.co.jp/products/vision/vision-sensor/cv-x100/)さんの外観検査装置については、個人的にはとてもおすすめ機材です。カメラやコントローラーの性能は申し分ないですし、UI回りがとてもよくできているので、合否判定のパラメータ設定などもとても簡単です。信頼性だって抜群です。型番は少し違いますが、実際に導入して使ってもいます。
外観検査を全くやったことのない方は一度このような専用の装置を使って経験してみる方が理解が早いと思います。

一方、Raspberry Piを使った自作検査装置は、どのような画像処理を行うのか、合否判定のアルゴリズムはどうするのかなど、ワークが変わるたびに自分で検討してプログラムしなければいけません。工場内で使うのであれば、オペレーターが簡単に操作するためのUIも作らなければなりません。さまざまな開発要素を考えると工数面のコストは多大です。

それでもopencvやRPiを使った手作り装置はとにかく安価であるという点と、カスタマイズの自由度の高さは魅力的です。その気になればRPiだけで外観検査以外の検査やワークの取り回し制御まですべて行うこともできるでしょう。
なにより1万円強で画像処理の基礎を学べつつ実用にも応用できるという環境には非常に価値があると思います。

opencvを使ったカスタムメイドの画像検査装置自体もそれほど珍しいものではないはずです。様々なカスタムマシンメーカーさんが専用の画像検査装置を制作している様です。（伝聞でしか知らないのですが）
opencvのライブラリは非常に多様で、使いこなせれば最新の画像処理アルゴリズムを試すことができます。今後利用するシーンが増えると思われるAIによる画像判定をするうえでも、前処理にopencvによる画像処理を行うケースも多く、改めてopencvの勉強をしておくのも価値があるのではないでしょうか。