OpenCVからカメラの解像度を切り替える

##はじめに
在宅勤務がはじまって、WEBカメラの価格が高騰しました。
この期間に買ったへなちょこカメラでopenCVでキャプチャするときの解像度とフレームレートの変更に苦労したのでまとめておきます。

##やったこと
Jetsonで、openCVを用いるときに、WEBカメラのコーデックを変えようとしたらCAP_PROP_FOURCCのsetでunhandled propertyといわれたので、v4l2srcを入力、appsinkを出力にgstreamerを利用して、これをvideoCaptureの引数文字列として指定することで、カメラのコーデックと解像度を変更しました。

##OpenCVでのフレームレート変更
通常は
[【Python】OpenCVでWebカメラのフレームサイズ、FPS設定](https://qiita.com/Kazuhito/items/cc6b8a0bd75cf9689bf9)にあるように

```python3:解像度変更
cap=videoCapture(0)
video_input.set(cv2.CAP_PROP_FPS, 30) 
video_input.set(cv2.CAP_PROP_FRAME_WIDTH, 1280)
video_input.set(cv2.CAP_PROP_FRAME_HEIGHT, 720) 
```
とかやると解像度の変更ができます。
このとき、変更先の解像度とFPSはカメラでサポートされているものである必要があり、このリストはv4l2-ctlコマンドなどで取得できます。以下は今回の対象のへなちょこカメラの出力です。

```
XavierNX:~/docker/darknet/work$ v4l2-ctl -d /dev/video1 --list-formats-ext
ioctl: VIDIOC_ENUM_FMT
	Index       : 0
	Type        : Video Capture
	Pixel Format: 'MJPG' (compressed)
	Name        : Motion-JPEG
		Size: Discrete 1920x1080
			Interval: Discrete 0.033s (30.000 fps)
		Size: Discrete 1280x720
			Interval: Discrete 0.033s (30.000 fps)
		Size: Discrete 640x360
			Interval: Discrete 0.033s (30.000 fps)
		Size: Discrete 320x180
			Interval: Discrete 0.033s (30.000 fps)
		Size: Discrete 1920x1080
			Interval: Discrete 0.033s (30.000 fps)

	Index       : 1
	Type        : Video Capture
	Pixel Format: 'YUYV'
	Name        : YUYV 4:2:2
		Size: Discrete 640x360
			Interval: Discrete 0.033s (30.000 fps)
		Size: Discrete 640x360
			Interval: Discrete 0.033s (30.000 fps)
		Size: Discrete 640x360
			Interval: Discrete 0.033s (30.000 fps)
		Size: Discrete 640x360
			Interval: Discrete 0.033s (30.000 fps)
		Size: Discrete 640x360
			Interval: Discrete 0.033s (30.000 fps)

XavierNX:~/docker/darknet/work$ 

```
v4l2-ctlがなければ、apt install v4l-utilsでインストールが可能です。

##問題点

- 問題点1：OpenCVがYUYVでカメラをオープンしようとする  
上記の出力例だと、OPENCVは、YUYVの1個目（1個目かどうかは自信ないです）の640ｘ360-30FPSでオープンしようとすることにあります。
この挙動を変更し、MJPG形式にカメラを切り替えない限り、このへなちょこカメラでは640ｘ360以外の解像度が設定できません。
- 問題点2：[VideoCaptureProperties](https://docs.opencv.org/3.4/d4/d15/group__videoio__flags__base.html#gaeb8dd9c89c10a5c63c139bf7c4f5704d)ではCAP_PROP_FOURCC でフォーマット変更ができることになっていますが、やってみるとunhandled propertyとなりJetson上では変更できません。

## 問題点2について
[OpenCVのカメラ読み込みを高速化し、遅延時間も短くする](https://qiita.com/iwatake2222/items/b8c442a9ec0406883950)などを参照すると

```python3:解像度変更
import cv2

cap=cv2.VideoCapture(1)
cap.set(cv2.CAP_PROP_FOURCC, cv2.VideoWriter_fourcc('M', 'J', 'P', 'G'));
video_input.set(cv2.CAP_PROP_FPS, 30) 
video_input.set(cv2.CAP_PROP_FRAME_WIDTH, 1280)
video_input.set(cv2.CAP_PROP_FRAME_HEIGHT, 720) 

while True:
    ret, frame_read = cap.read()
    cv2.imshow('Read',frame_read )
    cv2.waitKey(30)
cap.release()
```
ってやると動く気がします。
しかしなぜか、やってみるとJETSON上では

```
[ WARN:0] global /home/nvidia/host/build_opencv/nv_opencv/modules/videoio/src/cap_gstreamer.cpp (1184) setProperty OpenCV | GStreamer warning: GStreamer: unhandled property
```
っていわれ、MJPGに切り替えることができません。
実際に[OpenCV4.11のgitのCAP_gstreamer.cpp](https://github.com/opencv/opencv/blob/4.1.1/modules/videoio/src/cap_gstreamer.cpp)を見ると set関数ではCAP_PROP_FORCCは定義されておらずunhandled propertyにおちるようにかかれている気がします。
MACとかでやると動くのでvideoCapture でVIDEOデバイスをあけるときの実装が別のコードを利用するような気もしますが、Jetsonでの動作はコード上は As designedな動作に見えます。

#解決策
この道をすすむことはあきらめ、v4l2srcとappsinkをgstreamerで指定することで、コーデック解像度、FPSの変更を行いました。

```python3:解像度変更v4l2src
import cv2

src = 'v4l2src device=/dev/video1 ! image/jpeg,width=1280, height=720, framerate=(fraction)30/1 !jpegdec !videoconvert ! appsink'

cap=cv2.VideoCapture(src)
while(cap.isOpened()):
    ret, frame_read = cap.read()

    cv2.imshow('Read',frame_read )
    cv2.waitKey(30)
cap.release()

~             
```

他にもgstreamerのvideoinput部分を書き換えるといろいろできて楽しそうです。
[v4l2srcのドキュメント](https://gstreamer.freedesktop.org/documentation/video4linux2/v4l2src.html?gi-language=c)
とか、gst-launch-1.0にのせるパラメータで検索するといろいろ出てきます。