Spresense と Processing を使ってタイムラプス映像にチャレンジしてみました。次のような手順でやってみることにしました。

(1) カメラを接続した Spresense メインボードのUART2 をPCに接続
(2) Processing から撮影を開始を指示
(3) Spresense が撮影後に、UART2で画像をPCヘ送信
(4) Processing が受信し、ファイル保存ならびに画面を表示
(5) いくつか画像を取得したら ffmpeg で動画に変換

例によって動画にしていますので、お時間のある方はこちらをどうぞ。





Spresense と PC の接続にはFTDI製のUART-USB変換チップを乗せた変換ボードを使っています。PCには、Spresense用のスケッチを流し込むためと画像転送のための2つUSBケーブルを接続することになります。





Spresenseのコードは比較的シンプルです。Processingから’S'が来ると撮影が開始するようになっています。Arduinoだとカメラを扱おうとすると大変ですが、Spresenseだと楽なもんですね。


#include

#define BAUDRATE (115200)
#define BAUDRATE2 (2000000)

void setup()
{
Serial.begin(BAUDRATE);
Serial2.begin(BAUDRATE2);
while (!Serial) {};
while (!Serial2) {};

Serial.println("Prepare camera");
theCamera.begin();

Serial.println("Set Auto white balance parameter");
theCamera.setAutoWhiteBalanceMode(CAM_WHITE_BALANCE_DAYLIGHT);
theCamera.setStillPictureImageFormat(
CAM_IMGSIZE_QUADVGA_H,
CAM_IMGSIZE_QUADVGA_V,
CAM_IMAGE_PIX_FMT_JPG);
}

void loop()
{
while (Serial2.available() <= 0);
// taking a picture is started by receiving 'S'
if (Serial2.read() != 'S') return;

Serial.println("call takePicture()");
CamImage img = theCamera.takePicture();

if (!img.isAvailable()) {
Serial.println("take image failure");
return;
}

Serial.println("start transfer");
char* buf = img.getImgBuff();
for (int i = 0; i < img.getImgSize(); ++i, ++buf) {
Serial2.write(*buf);
}
Serial.println("end transfer");
}



Processing のスケッチは若干複雑です。キーボードを押すとSpresenseが撮影を開始し、マウスを押すと画像をセーブし表示するようにしています。


import processing.serial.*;
import java.io.*;
Serial myPort;

OutputStream output;
PImage img;
String filename;
boolean started = false;
int file_number = 0;
int serialTimer = 0;
int nodata_counter = 0;

void setup() {
size(1280, 960); background(0);
printArray(Serial.list());
// when debugging, you may have 2 serial ports
myPort = new Serial(this, Serial.list()[1], 2000000);
// myPort = new Serial(this, Serial.list()[0], 2000000);
myPort.clear();
}

void draw() {
try {
if (started == false) return;

// The serial of Processing communicates every 50msec
if (millis() - serialTimer > 50) {
serialTimer = millis();
if (myPort.available() <= 0) {
++nodata_counter;
return;
}
while (myPort.available() > 0) {
output.write(myPort.read());
}
nodata_counter = 0;
}
} catch (Exception e) {
e.printStackTrace();
return;
}
}

void keyPressed() {
started = true;
filename = "foge" + String.format("%03d", file_number) + ".jpg";
output = createOutput(filename);
++file_number;
myPort.write('S');
myPort.clear();
}

void mousePressed() {
println("nodata_counter: " + String.format("%d", nodata_counter));
// wait 100msec after receiving all jpg data
if (nodata_counter < 2 || started == false) {
return;
}

try {
output.flush();
output.close();
} catch (Exception e) {
e.printStackTrace();
return;
}

img = loadImage(filename);
image(img, 0, 0);
started = false;
}



複雑になってしまっている原因は、Processing のシリアルが50msec 毎に処理するようになっているためです。連続でデータを受けとらないため、バッファが空になったのを転送終了と判定できません。

画像の転送が終わったかどうかは、バッファが”しばらく”空になったかどうかで判定しています。

台風の影響で雲の流れが速かったので、雲の様子をとることにしました。二つもUSBをつなぐのは面倒なので電池駆動にしました。





実際にとれた画像がこちらです。このような雲の画像を約80枚ほど撮影しました。台風通過後だったので流れる雲の良い画がとれました。





これを動画にするには、ImageMagick に含まれる ffmpeg を使います。以下のコマンドで変換できます。ImageMagick はこちらで入手できます。



$ ffmpeg -f image2 -r 5 -i foge%03d.jpg -r 30 -s 1280x960 -vf format=yuv420p -vcodec libx264 -an video.mp4



動画はこちらで確認できます。短い時間しか撮っていませんが、思ったよりも楽しいものですね。





これでPCとの連携ができたので、次は動体検出などもっと高度なことができるようになるかも知れません。
(^^)/~


<関連記事>
SPRESENSEカメラのモニターをProcessingで作ってみた!
https://makers-with-myson.blog.so-net.ne.jp/2019-09-08






FTDI USBシリアル変換アダプター(5V/3.3V切り替え機能付き)

  • 出版社/メーカー: スイッチサイエンス(Switch Science)
  • メディア: おもちゃ&ホビー