簡単にできる!気軽に使える!お遊びデジカメ
折角、活用コンテストなので、Spresenseで画像認識を…と思っていたのですが、公事・私事で取れる時間が減ってしまい、更にはまってしまい、致し方なく路線変更をしました(お恥ずかしい限り…)。
簡単にできる物にしたはずなのに、思ったより手こずってしまい、コンテスト締め切りにギリギリの投稿となりました。
今後、色々な方が画像認識の発表されると思いますので、参考にさせてもらうこととします。
材料
部品 | 個数 | 値段 | 購入先 |
---|---|---|---|
SPRESENSEメインボード[CXD5602PWBMAIN1] | 1 | ¥6,050 | スイッチサイエンスなど |
SPRESENSE拡張ボード[CXD5602PWBEXT1] | 1 | ¥3,850 | スイッチサイエンスなど |
SPRESENSEカメラボード [CXD5602PWBCAM1] | 1 | ¥3,850 | スイッチサイエンスなど |
B-stem 4CM01 SPRESENSE用4chマイク基板 | 1 | ¥3,300 | スイッチサイエンスなど |
モノパ! 2.2インチ TFT 液晶ディスプレイ 240x320 ILI9341 | 1 | ¥1,980 | Amazonなど |
※B-stem 4CM01 SPRESENSE用4chマイク基板のマイク基板は、今回は使っていません。TFT液晶(SDカードスロット付)をメインボードにうまく配線できれば、拡張ボードやB-stem 4CM01 SPRESENSE用4chマイク基板は不要です(コストダウンできそうね)。
この他に、USB電源+USBマイクロ用コード、マイクロSDHCカードが必要です。ケースは、ご自由に。
プログラム紹介
下記のプログラムは、Sonyのサンプルプログラム(camera)をチューンナップしたものです。SW5(D03)がシャッター、SW4(D04)がモード順送り、SW3(D05)はモード逆送りとなっています。SW3/4を押すと、画面の表示が通常⇔白黒⇔セピア⇔反転⇔(通常)に変わります。
みなさんの開発環境によっては、TFT液晶用のZIPファイル(Adafruit_GFX/Adafruit_ILI9341)も必要になる場合があります。こちらのサイト(https://developer.sony.com/develop/spresense/docs/arduino_tutorials_ja.html#_tutorial_camera)をご参照ください。
お遊びカメラの例(Spresense用)
#include <SDHCI.h>
#include <stdio.h>
#include <Camera.h>
#include <SPI.h>
#include <Adafruit_GFX.h>
#include <Adafruit_ILI9341.h>
#define TFT_CS -1
#define TFT_RST 8
#define TFT_DC 9
Adafruit_ILI9341 tft = Adafruit_ILI9341(&SPI, TFT_DC, TFT_CS, TFT_RST);
SDClass theSD;
int take_picture_count = 0;
int eff_no;
CamErr err;
CamImage img;
void CamCB(CamImage _img)
{
if (_img.isAvailable())
{
_img.convertPixFormat(CAM_IMAGE_PIX_FMT_RGB565);
tft.drawRGBBitmap(0, 0, (uint16_t *)_img.getImgBuff(), 320, 240);
}
else
{
Serial.println("Failed to get video stream image");
}
}
void setup()
{
eff_no = 0; //画像効果NONE
tft.begin(40000000);
tft.setRotation(3);
pinMode(LED0,OUTPUT); //シャッター動作表示LED
pinMode(3,INPUT_PULLUP); //D3:シャッター
pinMode(4,INPUT_PULLUP); //D5:mode+
pinMode(5,INPUT_PULLUP); //D5:mode-
Serial.begin(115200);
while (!Serial)
{
; /* wait for serial port to connect. Needed for native USB port only */
}
/* Initialize SD */
while (!theSD.begin())
{
tft.setCursor(0, 0);
tft.setTextColor(ILI9341_RED);
tft.setTextSize(2);
tft.println("Insert SD card!!");
}
Serial.println("Prepare camera");
err = theCamera.begin();
err = theCamera.startStreaming(true, CamCB);
err = theCamera.setAutoWhiteBalance(true);
err = theCamera.setAutoISOSensitivity(true);
err = theCamera.setAutoExposure(true);
err = theCamera.setStillPictureImageFormat(
CAM_IMGSIZE_QUADVGA_H,
CAM_IMGSIZE_QUADVGA_V,
CAM_IMAGE_PIX_FMT_JPG);
}
void loop()
{
if (!digitalRead(3)){
Serial.println("call takePicture()");
digitalWrite(LED0,HIGH);
rtry:
img = theCamera.takePicture();
if (img.isAvailable())
{
//ファイル名の処理
char filename[16] = {0};
sprintf(filename, "PICT%03d.JPG", take_picture_count);
Serial.print("Save taken picture as ");
Serial.print(filename);
Serial.println("");
theSD.remove(filename);
File myFile = theSD.open(filename, FILE_WRITE);
myFile.write(img.getImgBuff(), img.getImgSize());
myFile.close();
//ファイル名の数字の所をカウントアップする
take_picture_count++;
delay(500);
}
else
{
// 2回に1回は、img.isAvailable()に行かない場合があり、強制的に再度 img = theCamera.takePicture();から実行するようにしています。
goto rtry;
}
digitalWrite(LED0,LOW);
} else if (!digitalRead(4)){
eff_no++;
if (eff_no > 3) {eff_no = 0;}
switch(eff_no){
case 0:
//通常モード
err = theCamera.setColorEffect(CAM_COLOR_FX_NONE );
break;
case 1:
//白黒モード
err = theCamera.setColorEffect(CAM_COLOR_FX_BW );
break;
case 2:
//セピアモード
err = theCamera.setColorEffect(CAM_COLOR_FX_SEPIA );
break;
case 3:
//反転モード
err = theCamera.setColorEffect(CAM_COLOR_FX_NEGATIVE );
break;
default:
break;
}
delay(1000);
} else if (!digitalRead(5)){
eff_no--;
if (eff_no < 0) {eff_no = 3;}
switch(eff_no){
case 0:
//通常モード
err = theCamera.setColorEffect(CAM_COLOR_FX_NONE );
break;
case 1:
//白黒モード
err = theCamera.setColorEffect(CAM_COLOR_FX_BW );
break;
case 2:
//セピアモード
err = theCamera.setColorEffect(CAM_COLOR_FX_SEPIA );
break;
case 3:
//反転モード
err = theCamera.setColorEffect(CAM_COLOR_FX_NEGATIVE );
break;
default:
break;
}
delay(1000);
}
}
カメラの動作を解説
・theCamera.begin():カメラを開始。
・theCamera.startStreaming(true, CamCB):ストリーミングを開始します。TFT液晶にカメラの映像をリアルタイム表示させたかったので、使っています。
・theCamera.setAutoWhiteBalance(true):自動ホワイトバランス
・ theCamera.setAutoISOSensitivity(true):自動ISO感度
※手動で切り替えが可能で、ISO25~1600の19ポジションが設定可能です。手動で切り替えすると、感度によってはTFTの映像が見づらくなるので、Autoにしました。
・theCamera.setAutoExposure(true):自動露光調整
・theCamera.setStillPictureImageFormat:静止画(写真)用の画サイズやピクセルフォーマットを設定。
・theCamera.setColorEffect(CAM_COLOR_FX_** ):画像効果設定。
------他にもHDRの機能も付いています(今回は未使用)。残念ながら、オートフォーカスは付いていないので、手振れしまくりです。手振れしないように、撮影の際はしっかりカメラを持ちましょう。
工夫した所
公式のサンプルをパクったので工夫した所はないでしょう?というツッコミがあるかも知れませんが、意外や意外。色々な機能が付いているので簡単じゃないかとタカをくくっていたのですが、結構、難儀していたのですよ。
・公式のサンプルでは、シャッターに割り込み処理を使っていましたが、何回か押していると、保存されないケースが。私の環境では、2回に1回は、img.isAvailable()に行かない場合が確認できました。goto命令を使い、強制的に再度 img = theCamera.takePicture();から実行するようにしています。
・モード切替にも割り込みを使いましたが、何回か押していると、全く動作を受け付けなくなる症状が発生しました。Serialでモニターすると、「PANIC!」なる表示が。
割り込み処理をやめ、ポーリングでボタンが押されたのを監視しています。「PANIC!」は出なくなったので、サンプルにあったSerialのモニター機能を一部削除しました。
割り込み処理の中に、カメラ用の関数を入れていたので、使い方がよくなかったようです(カメラ用の関数を入れなければ、「PANIC!」は表示されなくなった)。
・画像効果設定には、色々なモードがあるものの、私の環境では、なぜか一部のモードでは再現性が悪かったり、全く動作しなかったりしていたので(何でだろ?)、再現性がよいものだけ(NONE/白黒/セピア/反転)に絞って、機能を追加しています。
とはいえ、画像に趣のあるデジカメになりました。
簡単な命令で、デジカメができてしまう所がとにかく凄い! 子供向けのセミナーの教材なんぞにも使えそうではないですか。
ケースをボール紙や段ボールなんかで作ったりして。紙ならば、子供たちが思い思いに作って、楽しむことができますものね
投稿者の人気記事
-
kipopytokyo
さんが
2022/09/19
に
編集
をしました。
(メッセージ: 初版)
ログインしてコメントを投稿する