生産現場IoTへの挑戦 #08.5　～オートフォーカスUSBカメラをマニュアルフォーカスで使う～

# 1.はじめに
この記事は、[「生産現場IoTへの挑戦 #08　～Raspberry PiとUSBカメラで外観検査装置を作る　前編～」
](https://qiita.com/airpocket/items/493fd7fffdf2604f6c4c)の補足記事です。
opencvによる画像処理を行うにあたり、オートフォーカスカメラでは意図せぬピンボケや、フォーカシング時間のロスが発生するためマニュアルフォーカスカメラが必要と考えていました。しかしながらv4l2の機能をよく調べるとオートフォーカス機能付きのカメラでもv4l2のAPIでマニュアルコントロールできたためその確認結果をまとめます。
opencvやv4l2のインストールやRaspberry PiのOSなどについては[「生産現場IoTへの挑戦 #08」](https://qiita.com/airpocket/items/493fd7fffdf2604f6c4c)を参照してください。

# 2.結論
v4l2を使用すると、オートフォーカス機能付きUSBカメラのフォーカスをマニュアルコントロールできました。最短焦点距離は約10mmとかなり優秀です。
しかしながら、すべてのオートフォーカス機能付きカメラがv4l2でマニュアルコントロールできるとは限りませんのでご注意下さい。

掲載したデモコードを使い、キーボード入力でフォーカス調整した動画です。

<iframe width="560" height="315" src="https://www.youtube.com/embed/Q5cXw1kXwWI" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>

検査用カメラとして使用する場合、画角や最小焦点距離なども重要なスペックとなります。カメラの選定時には、カメラスペックが自分の使用目的に合っているかどうかよく確認しましょう（とは言え、USBカメラのレンズ仕様などはそこまで詳細にオープンされていませんので、買ってみてテストするのが早そうです）

**元記事のコメントでも指摘されていましたが、市販のウェブカメラのセンサーは産業用カメラのセンサーと比較して耐久度や信頼性が数段落ちます。連続使用により取得できる画像の質が変化したり、連続稼働で故障しやすいなどの問題が予想されますので事前によく確認してください。**

# 3.仕様した機材
今回、オートフォーカス機能付きカメラとしてELECOMの[「UCAM-C820ABBK」](https://www.amazon.co.jp/dp/B08LN1LZX1/ref=cm_sw_r_tw_dp_H0825D69CRH6QKGJPJP6)を使用しました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/496179/d9c9e164-4b71-2afd-d061-86011ca0f51f.png)

早速Raspberry Piに接続して確認します。lsusbコマンドで見ると、Sunplus Innovation Technology Inc. FHD Camera Microphoneとして認識されています。
制御可能な項目をv4l2-ctl -L で確認すると、focus_autoとfocus_absoluteの項目がありました。どうやらこのカメラはフォーカスをマニュアル制御できる様です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/496179/416fb07b-4aa7-ee10-b364-77df1d8ed2e6.png)

# 4.テストコード
以下のテストコードでfocus_absoluteを変更して動作を確認しました。
focus_absolute = 1023で無限遠、0にすると最短焦点距離約10mmでした。オートフォーカスだとまず無理なのですが、マニュアルだとものすごい近距離でも合焦するんですね。前回使ったカメラもほぼ同じ最短焦点距離だったのでUSBカメラのレンズってだいたいこんな性能なのでしょうか？

```python
#v4l2によるオートフォーカスカメラのマニュアル制御テスト
#q/aでフォーカスざっくり変更、w/sで微動

import cv2
import os

focus=100

#カメラのフォーカス、露出、ホワイトバランスをマニュアル制御化
os.system('v4l2-ctl -d /dev/video0 -c focus_auto=0')
os.system('v4l2-ctl -d /dev/video0 -c focus_absolute='+str(focus))
os.system('v4l2-ctl -d /dev/video0 -c exposure_auto=1 -c exposure_absolute=300')
os.system('v4l2-ctl -d /dev/video0 -c white_balance_temperature_auto=0')
os.system('v4l2-ctl -d /dev/video0 -c white_balance_temperature=4600')

#解像度パラメータ
WIDTH  = 1280  #320/640/800/1024/1280/1920
HEIGHT = 720   #240/480/600/ 576/ 720/1080

#カメラ入力インスタンス定義
capture = cv2.VideoCapture(0)

#解像度変更
capture.set(cv2.CAP_PROP_FRAME_WIDTH, WIDTH)
capture.set(cv2.CAP_PROP_FRAME_HEIGHT, HEIGHT)

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 == ord("q"):
        focus += 100
        if focus > 1023:
            focus = 1023
        os.system('v4l2-ctl -d /dev/video0 -c focus_absolute='+str(focus))
        print("focus = ", focus)

    elif key == ord("w"):
        focus += 10
        if focus > 1023:
            focus = 1023
        os.system('v4l2-ctl -d /dev/video0 -c focus_absolute='+str(focus))
        print("focus = ", focus)

    elif key == ord("a"):
        focus -= 100
        if focus < 0:
            focus = 0
        os.system('v4l2-ctl -d /dev/video0 -c focus_absolute='+str(focus))
        print("focus = ", focus)

    elif key == ord("s"):
        focus -= 10
        if focus < 0:
            focus = 0
        os.system('v4l2-ctl -d /dev/video0 -c focus_absolute='+str(focus))
        print("focus = ", focus)

    elif key == 27: #ESC
        break
```