raspiでautofocusを実装してみる(1)

# 購入商品

カメラを使ってみたかったのでarducamを購入しました．

https://www.amazon.co.jp/Arducam-Arducam%E3%82%AA%E3%83%BC%E3%83%88%E3%83%95%E3%82%A9%E3%83%BC%E3%82%AB%E3%82%B9%E3%82%AB%E3%83%A1%E3%83%A9%E3%80%81Raspberry-Pi%E3%82%AB%E3%83%A1%E3%83%A9%E3%83%A2%E3%82%B8%E3%83%A5%E3%83%BC%E3%83%AB%E7%94%A8%E3%82%AA%E3%83%BC%E3%83%88%E3%83%95%E3%82%A9%E3%83%BC%E3%82%AB%E3%82%B9%E3%80%81%E9%9B%BB%E5%8B%95%E3%83%95%E3%82%A9%E3%83%BC%E3%82%AB%E3%82%B9%E3%83%AC%E3%83%B3%E3%82%BA%E3%80%81OV5647-5MP-1080P/dp/B07SN8GYGD

こちらの商品です．3000くらいで電動フォーカスをプログラミングできます．

# Documentsとgithub
https://www.arducam.com/raspberry-pi-camera/autofocus/

https://github.com/ArduCAM/RaspberryPi/tree/master/Motorized_Focus_Camera

https://picamera.readthedocs.io/en/release-1.13/

https://www.raspberrypi.com/documentation/accessories/camera.html

# 初期設定
最初はgithub，説明書にあるとおりに進めていく

```
sudo raspi-config
```
からcameraをenableにする
次にサンプルで必要なopencvを入れて，i2cをonにする(詳しくはenable_i2c_vcを参照)．

```
sudo apt-get install python-opencv
git clone https://github.com/ArduCAM/RaspberryPi.git
cd RaspberryPi/Motorized_Focus_Camera
sudo chmod +x enable_i2c_vc.sh
./enable_i2c_vc.sh
```

rebootするかと言われるのでyと押す．完了

# サンプルコード実行

## libarducam_vcm.so
遊ぶために関数を理解する．
実装がわからない関数は

```
arducam_vcm.vcm_write(val)
arducam_vcm.vcm_init()
```
だけであるから問題はない．[^1]
簡単にいうとvcm_writeが焦点距離を書き込む関数，vcm_initが初期化する関数のよう．

## libarducam_keyboard.so
keyboardの入力を読み取る関数．これはcfileがあるので書き直し可能．
矢印キーを頑張って処理するために色々書いたり入力させた文字をターミナル上で表示しないようにするためにしている．
upとdownの時のreturnが気に入らないので128と129に書き直した．

<details><summary>libarducam_keyboard.c</summary><div>

```c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>

#define UP 128
#define DOWN 129

int arducam_get_key()
{
    int key_value;
    struct termios new_config;
    struct termios old_config;

    tcgetattr(0, &old_config);
    new_config = old_config;
    new_config.c_lflag &= ~(ICANON | ECHO);
    tcsetattr(0, TCSANOW, &new_config);
    key_value = getchar();
    tcsetattr(0, TCSANOW, &old_config);
    return key_value;
}
int processKeyEvent(void)
{
    int keyVal = 0;
    keyVal = arducam_get_key();
    if (keyVal == 27)
    {
        keyVal = arducam_get_key();
        if (keyVal == 91)
        {
            keyVal = arducam_get_key();
            if (keyVal == 65)
            {
                return UP;
            }
            if (keyVal == 66)
            {
                return DOWN;
            }
        }
    }
    else
    {
        return keyVal;
    }
}
//test
int main(int argc, char **argv)
{
    int val = processKeyEvent();
    printf("val = %d\r\n", val);
    printf("%d\n", 'a');
}
```
</div></details>

## Motorized_Focus_Camera_Preview.py
私の環境だと普通に動きませんでした．(python3,raspi4Bの8GB,macからrealvnc)
コードを修正 and  終了のif文がないので追加

<details><summary>Motorized_Focus_Camera_Preview.py</summary><div>

```python
import os
import time
import _thread as thread
from ctypes import *
import picamera
keyboard = CDLL('./lib/libarducam_keyboard.so')
arducam_vcm = CDLL('./lib/libarducam_vcm.so')
UP = 128
DOWN = 129
SAVE = 115
focus_val = 512
step = 10

if __name__ == "__main__":
    try:
        # vcm init
        arducam_vcm.vcm_init()
        camera = picamera.PiCamera()
        camera.preview_fullscreen = False
        preview = camera.start_preview()
        # set windows size
        preview.window = (0, 0, 800, 600)
    except:
        os.system(
            """ps aux | grep raspistill | grep -v grep | awk '{ print "kill -9", $2 }' | sh""")
        raise BaseException("resource error")

    print("Please press up and down to adjust focus.")
    while True:
        keyVal = keyboard.processKeyEvent()
        time.sleep(0.01)
        if keyVal == UP:
            print("UP")
            focus_val = focus_val+step
            if focus_val > 1023:
                focus_val = 1023
            print(focus_val)
            arducam_vcm.vcm_write(focus_val)
        if keyVal == DOWN:
            print("DOWN")
            focus_val = focus_val-step
            if focus_val < 0:
                focus_val = 0
            print(focus_val)
            arducam_vcm.vcm_write(focus_val)
        if keyVal == ord('s'):
            print("save test.jpg")
            camera.capture("test.jpg")
        if keyVal == ord('a'):
            print("END")
            break
    camera.close()
```
</div></details>

ここから読み取るにfocus_valは0から1023までで考えれば良い

## トラブルリスト
・ tightvncserverではpreviewが表示できない

realvncを使う．
参考サイト
https://www.youtube.com/watch?v=dbBWyeHbGs0
https://ryoichi0102.hatenablog.com/entry/vnc-viewer-cannot-currently-show-the-dekstop

・cui版ではpicameraとかのインストールで色々エラーが出る
desktop版をインストールする．

続きはコントラストAFの実装

[^1]: これに関連したissue．逆アセンブリしてる．https://github.com/ArduCAM/RaspberryPi/issues/47