前回、SPRESENSEとProcessingを使ってタイムラプスカメラを作ってみました。そこに、いつもチェックしているスタックオーバーフローで次のような問いかけが!



カメラのリアルタイムのプレビューをPCの画面に表示する方法

SPRESENSEのカメラで現在映しているプレビューを、PCの画面に表示させることは可能でしょうか。接続方法は、できれば、USB(メインボード側のSerial)が良いです。




ん!?これは、できそうな気がするぞ。SPRESENSEとProcessingを使えば…。

ということでチャレンジしてみました!

接続はリクエスト通りシリアルケーブル一本で。SPRESENSE からプレビュー画像を転送し、PC上の Processing でデータを受けて画像を表示します。





余談ですが、少し前に3Dプリンタで作ったカメラホルダをつけてみました。カメラとUSBケーブルの干渉がなくなり、少しすっきりしました。





今回作成したスケッチです。すごくシンプルなのですが、Processing のシリアルはなかなか使いこなすのが大変で苦労しました。Windows + Java のためか、リアルタイムには程遠く、安定した通信をするために、ところどころ工夫が必要でした。


SPRESENSEのスケッチ

#include &ltCamera.h>

#define BAUDRATE 2000000

void CamCB(CamImage img) {

if (img.isAvailable() == false) return;

while (Serial.available() <= 0);
// taking a picture is started by receiving 'S'
if (Serial.read() != 'S') return;
delay(1); // wait for stable connection

digitalWrite(LED0, HIGH);
img.convertPixFormat(CAM_IMAGE_PIX_FMT_RGB565);
char *buf = img.getImgBuff();
for (int i = 0; i < img.getImgSize(); ++i, ++buf) {
Serial.write(*buf);
}
digitalWrite(LED0, LOW);
}

void setup() {

Serial.begin(BAUDRATE);
while (!Serial) {};

theCamera.begin();
theCamera.startStreaming(true, CamCB);
theCamera.setAutoWhiteBalanceMode(CAM_WHITE_BALANCE_DAYLIGHT);
}

void loop() {
/* do nothing here */
}



Processingのスケッチ

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

PImage img;
final static int WIDTH = 320;
final static int HEIGHT = 240;
boolean started = false;
int serialTimer = 0;
int total = 0;
int x = 0;
int y = 0;

void setup() {
size(320, 240);
background(0);
img = createImage(WIDTH, HEIGHT, RGB);
myPort = new Serial(this, Serial.list()[0], 2000000);
myPort.clear();
println("setup finished");
delay(2000); // wait for stable connection
}

void draw() {

if (started == false) {
started = true;
println("start");
myPort.write('S');
myPort.clear();
total = 0;
delay(10);
return;
}

// To get stable connection, please adjsut this interval
final int interval = 1;
if (millis() - serialTimer > interval) {
serialTimer = millis();
if (myPort.available() <= 0) return;

while (myPort.available() > 0) {
char lbyte = (char)myPort.read();
char ubyte = (char)myPort.read();
x = total % WIDTH;
y = total / WIDTH;
int value = (ubyte << 8) | (lbyte); // RGB565 format
char r = (char)(((value & 0xf800) >> 11) << 3);
char g = (char)(((value & 0x07E0) >> 5) << 2);
char b = (char)((value & 0x001f) << 3);
color c = color(r, g, b);
img.set(x, y, c);
++total;

if (total >= WIDTH*HEIGHT) {
println("end");
myPort.clear();
started = false;
total = 0;
image(img, 0, 0);
break;
}
}
}
}



動かしてみると、パフォーマンスは、おおよそ 0.9-0.8fps 。理論的には、 (2000000/8) bytes / 153,600 bytes / =1.6 fps 位のはずですが、Processing の応答性が思いのほか悪く、あまり良い数字は出ませんでした。

実際の動きはこちらの動画で見ることができます!





パフォーマンスはいまいちですけど、この小さなカメラで画面を見れるのはいいですね。次はプレビューではなくて、もう少し大きな画像を転送してみようかな…。
(^^)/~


<参考記事>
SPRESENSEとProcessingでタイムラプスに挑戦! 
https://makers-with-myson.blog.so-net.ne.jp/2019-08-17