の続きで画像データの取得に関してです。
web_camサンプルではstartCameraServer()の一行でWebCameraとしてストリーミングしてくれるので画像データはいったいどこでどう処理されてるんだ???と疑問に思います。
カメラの初期化
データを取得する前にカメラの初期化が必要になります。
esp_camera_init()で行いますが、引数として渡す環境設定はTimer Camera Xでハード的に決まっている事なのでweb_camサンプルとほぼ同じになると思います。
void setup() {
.
.
camera_config_t config;
config.ledc_channel = LEDC_CHANNEL_0;
config.ledc_timer = LEDC_TIMER_0;
config.pin_d0 = Y2_GPIO_NUM;
config.pin_d1 = Y3_GPIO_NUM;
config.pin_d2 = Y4_GPIO_NUM;
config.pin_d3 = Y5_GPIO_NUM;
config.pin_d4 = Y6_GPIO_NUM;
config.pin_d5 = Y7_GPIO_NUM;
config.pin_d6 = Y8_GPIO_NUM;
config.pin_d7 = Y9_GPIO_NUM;
config.pin_xclk = XCLK_GPIO_NUM;
config.pin_pclk = PCLK_GPIO_NUM;
config.pin_vsync = VSYNC_GPIO_NUM;
config.pin_href = HREF_GPIO_NUM;
config.pin_sscb_sda = SIOD_GPIO_NUM;
config.pin_sscb_scl = SIOC_GPIO_NUM;
config.pin_pwdn = PWDN_GPIO_NUM;
config.pin_reset = RESET_GPIO_NUM;
config.xclk_freq_hz = 20000000;
config.pixel_format = PIXFORMAT_JPEG;
config.frame_size = FRAMESIZE_QVGA;
config.jpeg_quality = 10;
config.fb_count = 2;
// camera init
esp_err_t err = esp_camera_init(&config);
if (err != ESP_OK) {
Serial.printf("Camera init failed with error 0x%x", err);
return;
}
sensor_t *s = esp_camera_sensor_get();
// initial sensors are flipped vertically and colors are a bit saturated
s->set_vflip(s, 1); // flip it back
s->set_brightness(s, 1); // up the blightness just a bit
s->set_saturation(s, -2); // lower the saturation
.
.
}
変更するとすれば config.pixel_format、config.jpeg_quality、config.frame_size辺りではないでしょうか。
config.pixel_formatでは取得する画像データの形式を指定します。jpegの場合はconfig.jpeg_qualityで品質を指定します。config.frame_sizeでは画像サイズを指定します。
今回はjpegデータで欲しいのでこのままにします。config.frame_sizeは後で画像確認する時のためにサイズが小さいFRAMESIZE_QVGAを指定しています。
esp_camera_sensor_get()で得られたsensor_t構造体の関数ポインタを使用して撮像条件を指定できます。ここら辺は前回のweb_camサンプルを使ってGUIで条件を出してそれを適用する様にすればいいのではないでしょうか。とはいいつつ結構大変そうなのでweb_camでの設定から設定するコードを自動生成する様ななんかができればいいのではないかと思いますが、今回の本質ではないのでまたの機会にします。
画像データの取得
画像データはいったいどこでどう処理されてるんだ???と疑問に思いましたが、画像データを取得するにはesp_camera_fb_get()関数を使用するだけです。これを呼び出すと撮像したデータがcamera_rb_t構造体を通して得られます。なんと簡単。
camera_fb_t *fb = NULL;
fb = esp_camera_fb_get();
fb->lenで画像データのサイズが得られ、fb->bufが画像データのポインターとなっています。config.pixel_formatでPIXFORMAT_JPEGを指定しているのでjpegデータとして得られます。
大事なことは画像データは mallocなどで動的に確保されている様なので使い終わって不要になったらesp_camera_fb_return()で 解放してあげないといけない ことです。
これを忘れるとesp_camera_fb_get()の呼び出し2、3回目辺りでメモリ不足で再起動してしまいます。
esp_camera_fb_return(fb);
せっかくデータが得られても画像が見れないので、シリアル出力にrubyスクリプトを書き出しそれをコピペして実行すると見れる様にしてみました。
camera_fb_t *fb = NULL;
fb = esp_camera_fb_get();
size_t fb_len = 0;
fb_len = fb->len;
size_t blk_size = 60;
unsigned char inp[blk_size];
unsigned char out[blk_size * 15 / 10];
Serial.println("require 'base64'");
Serial.println("s = <<EOS");
for (int i = 0; i < fb_len; i += blk_size) {
encode_base64(&(fb->buf[i]), min(fb_len - i, blk_size), out);
Serial.printf("%s\n", out);
}
Serial.println("EOS");
Serial.println("File.write('image.jpg', Base64.decode64(s))");
Serial.println("system('open image.jpg')");
実行するとこんな出力が出るので、適当なrubyスクリプトファイルを作って貼り付けて実行すると画像が見れます。
require 'base64'
s = <<EOS
/9j/4AAQSkZJRgABAQEAAAAAAAD/2wBDAAoHCAkIBgoJCAkLCwoMDxkQDw4ODx8WFxIZJCAmJiQgIyIo
.
.
.
S0gFozzSAM0bqAF3Um6lYAzTc0wEopgXYcumak2muZljTHntUfllfu0rjDcy9RUiuDQ0IdijaaQH/9k=
EOS
File.write('image.jpg', Base64.decode64(s))
system('open image.jpg')
ソースコードはこちらに公開してます。