マイコン工作実験日記

Microcontroller を用いての工作、実験記録

AVIファイルについて調べる --その2

2012-05-31 22:11:21 | Weblog
引き続き、AVIファイル作成についてです。Windowsでプロパティ表示すらできなかった問題が解決。もちろん、再生もできるようになりました。ファイルにidx1リストを追加して、インデックス情報を与えてやったことで問題解決です。idx1は古いインデックス方式ですが、AVIファイルのリファレンスではオプションとされていたので不必要だろうと思っていました。ところが、Windowsでは必要なようです。結果として、作成するAVIファイルの構造は次のようになりました。
RIFF ('AVI '
      LIST ('hdrl'
            'avih'(<Main AVI Header>)
            LIST ('strl'
                  'strh'(<Stream header>)
                  'strf'(<Stream format>)
                 )
           )
      LIST ('movi'
            'dc00' (<JPEG data>)
            'dc00' (<JPEG data>)
            ...
            'dc00' (<JPEG data>)
           )
      'idx1' (<AVI Index>)
     )

AVIファイルの構造については、検索すればいくらでも説明がみつかりますので、できるだけ手抜きしてMotion JPEGファイルを作るための要点ということで整理しておきます。
  • OpenDMLに従わなくても再生できるAVIファイルを作成できるが、idx1リストがないとWindows環境ではプロパティも表示できない。
  • 製作中のカメラはマイクを持たないので、動画データには圧縮画像データだけが含まれ、音声データは無い。そのため、AVI headerにおいて dwStrems=1に設定。movi listにおいては、’00dc'のデータチャンクだけが含まれる。
  • Motion JPEGであることを示すために、strhのfccHandlerを'mjpg'に設定。また、strf中のbmiHeader.biCompressionで'MJPG'を指定する。
  • '00dc'のデータチャンクはワード境界に配置する。
  • OpenDMLのドキュメントを読むと, MS DIB specとかいう仕様で、「JPEGデータ部分では、SOIに続いてAPP0セグメントが配置され、その最初の4バイトを'AVI1'にする」というお約束があるらしい。試したところではAPP0で'JFIF'でも大丈夫だったし、APP0の代わりにAPP1のExifが入っていても再生できた。

自分の環境の都合上、MacOS上のQuickTime XならびにWindows 7上のMedia Playerで再生できることしか確認していません。他のプレーヤでは正しく再生できないことがあるかもしれません。

続いてSAM3Sにこのコードを移植する作業に入ります。

AVIファイルについて調べる

2012-05-30 12:20:12 | Weblog
AVIファイルを作成することにしたので、その準備作業としてJPEGファイルを連結してMotion JPEGのAVIファイルを試しにPC上で作ってみることにしました。まずはMSDNの資料で概要を把握。この資料にも書かれているように、OpenDMLという拡張があるようで、どうやら今ではこの拡張を使うのが普通らしい。手元にあるCanonのデジカメもAVIファイルを作成しますが、hexdumpして調べてみるとindxを使ったインデックスやodmlリストを使ったフレーム数を持っており、OpenDML拡張を用いているようです。

まずはOpenDML拡張無しで簡単なAVIファイルを作ってみようと考え、データ・チャンクのヘッダを付けた2種類のJPEGファイルを交互に合計10枚つなげて、先頭にRIFF/AVIヘッダーを付けた簡単なAVIファイルを作成。ところがQuickTimeで再生しようとすると不正なファイルだと言われるし、Windowsではプロパティすら見れないありさま。いろいろと試行錯誤して、ようやくとQuickTimeで再生できるようになりました。圧縮画像データが配置される00dcチャンクが、奇数アドレスから始まっていると再生できないようです。16ビット境界に配置するように必要ならばパディングするようにしたところ、再生できようになりました。32ビット境界に配置した方がさらに安全なのかもしれません。

QuickTimeでは再生できるようになったものの、Windows環境ではあいかわらずプロパティの表示すらできません。まだAVIファイルとして見なしてもらえる条件を満たしていないようです。ヘッダー情報が少なすぎるのかもしれません。手抜きせずにちゃんと、ヘッダー情報用意しないとダメかな。

インターバル撮影動画を目指す

2012-05-26 09:51:44 | CMOSカメラ
カメラ画像をJPEGとして保存することはできたので、次の段階として動画として保存する段階へ進むことにします。本来であれば、VGAサイズの画像を保存するところまでやってみたかったのですが、SAM3S4Bの48K RAMではメモリが不足してJPEGへの変換ができないので断念です。SAM3S8Bになると内蔵RAMが64Kに増えているので、なんとかできそうなのですが。

動画もファイルとして保存するわけですが、コンテナ形式としては比較的簡単そうなAVIファイルを選択することにします。Windows環境では古くから使われている形式ですし、手元にAVIを出してくれるデジカメがあることもこのファイル形式を選択する理由のひとつです。AVIファイルの中に実際に格納する画像の圧縮形式にはMotion JPEG (MJPG)形式を選択することとします。Motion JPEGは、JPEG画像を連続して並べることで、動画像を表現します。パラパラ漫画と同じですね。前の画像との差分をとったりというような処理は一切しない単純な方式なので、当然のことながら動画のサイズは大きくなってしまいます。それでも非圧縮の場合に比べれば1/4程度には圧縮できます。ファイルサイズが小さいことは、SDカードへの書き込み時間も短いことを意味します。何しろメモリ容量が限られているので、JPEG圧縮をしながら順次カードへの書き込みをおこなっています。書き込み用のバッファサイズを最低限に抑えるために、1セクタ(512バイト)分の書き込みデータが溜まったところで、カードへの書き込みを行っています。複数セクター分の書き込みを一度におこなった方が書き込み時間が短縮できるのですが、メモリ節約が最優先です。

このようにメモリ制約がキツイので、圧縮処理と書き込み処理を並行して実行させるためにダブルバッファリングを使うこともできません。そのため圧縮と書き込みには1秒程度の時間がかかっていまい、とてもじゃありませんが20fpsとかの速度で画像を記録することなんてできません。しかし、用途をインターバル撮影用途に限定してしまえば、圧縮/記録時間がかかっても問題になりません。マイコンで周期的に撮影、圧縮、記録を繰り返しながら動画ファイルを作成してやろうというわけです。植物や景色を継続的に撮影するのに利用できます。おもちゃとしては楽しめそうだと思っていたのですが、すでにキングジムからレコロという製品が発売されているのですね。知らなかった。自分が目指していたコンセプトそのものです。レコロがVGAサイズまでできるのにこちらはQVGAですので、解像度の点では負けていますがメゲずに作ってみることにします。

KING JIM インターバルレコーダー レコロ IR5オレ オレンジ
キングジム
キングジム


うーん、レコロって値段も手頃だなぁ。カミさんに「作るより安いじゃん」と言われてしまいそう。使う事よりも自作する過程を楽しみにしている自分ですら、「リファレンスに買ってみようか」と思ってしまいます。もちろん自作すれば、レコロには用意されていない機能を備えることも可能になります。例えば、
  • 撮影の開始、終了時刻を設定する。
  • ちゃんとファイル作成日付をつけられる。画像中に時刻を埋め込める。
  • 明るさセンサと連動して、昼間だけ撮影する。
いつも、そこまで完成度の高いものに仕上げる前に、違うことやり始めてしまうのですが。。。

Arduino Due

2012-05-22 21:19:52 | Weblog
先週、San Mateo Maker FairでATMELが新しい Arduino Dueのデモをしたらしい。Arduinoには興味無いので気に留めていませんでしたが、ATMELのプレスを読んだらDueはSAM3X8ベースだったんですね。これでSAM3シリーズも少しはメジャーになれるんでしょうか?やはり、新製品でもメジャーなのはATmega32U4ベースのArduino Leonardoでしょうかね。国内代理店でもLeonardoの予約が始まっており、6/1には出荷可能な様子もうかがえますし。

と、ちょっと自虐的になっていたところで気づいたことが。。Dueって、イタリア語では の意味なんですね。現在メジャーなArduinoボードはUnoだと思うんですが、こちらは1の意味です。ということは、DueはUnoを継いで次世代を切り開く製品になるのかも知れませんね。Dueは昨年すでに製品概要が発表されていたようですが、その時点ではSAM3Uを使用することになっていました。製品の販売開始を送らせたことでSAM3Uに代えてSAM4X8を使うことになったようですね。


libjpegを使ってExif情報を書き込む

2012-05-21 22:51:45 | Weblog
これまでは、画像中にRTCの日時を埋め込んでSDカードに保存していました。しかし、JPEGを生成することができるようになったので、次の段階としてExif情報として撮影日時を記録することにします。

IJGのlibjpegを普通に使って生成したJPEGファイルは、色空間としてYCbCrを使った場合には上図左側に示したようにSOIマーカーに続いてAPP0マーカのセグメントを持つJFIF形式となっています。一方、デジカメで生成されるExif情報を持つJPEGファイルではAPP0の代わりにAPP1マーカのセグメントを持ち、この中にExif情報が書かれています。つまり、Exifの規格にそったファイルを生成するためには、
  1. JFIFをもつAPP0セグメントを出力しないようにする。
  2. Exif情報を持つAPP1セグメントを代わりに出力する。
という、2つの処理を行う必要があります。libjpegを調べたところ、これらの処理をおこなうためのAPIがちゃんと用意されていました。コードはこんな感じになります。
 struct jpeg_compress_struct cinfo;
 uint8_t raster_buffer[QVGA_WIDTH*2];
 JSAMPLE jwbuffer[QVGA_WIDTH*3];
 JSAMPROW jrow;

 .....
 jpeg_create_compress(&cinfo);
 .....
 cinfo.input_components = 3;
 cinfo.in_color_space = JCS_YCbCr;       /* 入力の色空間をYCbCrに設定 */
 jpeg_set_defaults(&cinfo);
 jpeg_set_colorspace(&cinfo, JCS_YCbCr); /* 出力の色空間をYCbCrに設定 */

 cinfo.write_JFIF_header = FALSE;    /* JFIFヘッダ(APP0)を出力しない */

 jpeg_set_quality(&cinfo, 90, TRUE);
 jpeg_start_compress(&cinfo, TRUE);

 /* Exif情報 (APP1)を出力 */
 jpeg_write(marker(&cinfo, JPEG_APP0+1, &exif_info, sizeof(exif_info));

 jrow = jwbuffer;
 for (i = 0; i < cinfo.image_height;i++) {
    /* 1ライン分データをFIFOから読み出し */
    read_fifo(raster_buffer, cinfo.image_width);
    /* カメラからのYCbCr4:2:2データを YCbCr4:4:4に変換 */
    ycbcr422_to_ycbcr444(raster_buffer, jw_buffer);
    /* Libjpegを使って1ライン分を圧縮 */
    jpeg_write_scanlines(&cinfo, &jrow, 1);
 }
 jpeg_finish_compress(&cinfo);
 jpeg_destroy_compress(&cinfo);

まず、jpeg_compress_struct構造体の中のwrite_JFIF_headerをFALSEに設定します。これでJFIFを含むAPP0セグメントが出力されなくなります。入力/出力する色空間としてYCbCrを選択、ファイルサイズが16KBを超えるようにqualityとして90を指定しておいて、jpeg_start_compress()を呼びだします。そして、jpeg_write_marker()を使ってAPP1マーカーを持つセグメント情報(すなわちExif情報)を書き出します。この後で、jpeg_write_scanlines()を呼ぶ事でExif情報ならびに圧縮関連情報の後に圧縮画像データが続くことになります。



こうして作られたのが上の画像です。見かけ上はこれまでと変わりありませんが、Exif情報が追加されています。次のような情報を追加していますが、ほとんどのタグ情報は固定値であり動的に変化するのは撮影日時だけとなっています。
タグ名意味
Makeメーカ名sirius506
Model</rd>モデルsirius506 ov7670
DateTimeOriginal原画像データの生成日時YYYY:MM:DD HH:MM:SS
ExifVersionExifバージョン0230
MeteringMode測光方式不明
LightSource光源不明
ExposureMode露出モード露出自動
WhiteBalanceホワイトバランス自動

いやはや役に立たない値ばかりです。撮影データとしてはシャッタースピードとか絞りとかの値が欲しいところですが、どうやって求めればいいのかわかりません。Eye-FiでGoogle+に自動アップロードされたアルバム画像を見れば主要Exif情報を確認できますが、このように空欄が目立ちます。



近日登場予定

2012-05-16 22:46:11 | Weblog
毎週恒例、Mouser の新製品紹介ページをチェックしていたら、SAM4S近日登場!との記載が目に飛び込んできました。Mouserで買えるようになるのは嬉しいニュースではありますが、「まずはSAM3S在庫してくれよ!!」と突っ込みたいのが個人的な感想。

Digikeyを確認してみるも、SAM4Sについてはまだ音沙汰無い模様。しかし、7月出荷予定となっていたSAM3A/SAM3Xの出荷予定日がいつの間にか5月24日になっています。ていうか、来週じゃねーか。自分が使うとしたら、SAM3Aの100pinが物理的な限界かな。これが64pinだったら迷わずに買っておくところなんですが、100pinとなるとそれだけで心理的障壁が高くなっていまいます。

Eye-FiカードへのJPEGファイル書き込み

2012-05-15 12:27:32 | Weblog
前回の記事にも書いたように、Eye-Fiカードへの書き込まれた画像がWiFi経由で転送されるためには、いくつかの条件があります。これまでの経験から習得した、Eye-Fiカードを使ううえでの注意点を整理しておくことにします。
  1. 消費電力
    Eye-Fiカードは無線LAN機能内蔵なので、普通のSDカードと比べて当然消費電力が多くなります。Eye-Fiは1分周期で無線アクセスポイントと新しい画像の有無をチェックする仕様となっているようです。無用の場合には、カードへの電源供給を断った方が好ましいので、今回製作したボードではFETスイッチを設けてこれを制御できるようにしてあります。ところが、このスイッチを入れたとたんに、マイコンにリセットがかかるという問題に遭遇しました。原因は、カードへの電源供給開始にともなう突入電流によって電圧降下が発生し、BODがかかるためです。対策として手もちの47μHのインダクタをSDカードへのVccに追加することで、問題解決。普通の2GB SDカードを使っていた時は問題なかったので、Eye-Fiカードの違いを痛感。
  2. DCFに従う
    前回の記事にも書いたとおり、カードへのJPEGデータ保存に際してはDCFに従ったディレクトリ構造、ファイル名を持つ必要があります。最初、ファイル名の番号付けを間違えていたためにファイル名が8文字であるべきところが7文字になっていたために、ちっとも画像が転送されずハマってしまいました。
  3. ファイル長
    やはり16KB以下のサイズのファイルは転送されないという推測は当たっているようです。16KB以上のサイズの画像は全て転送されることが確認できました。圧縮時のqualityを80から90に変更することで対応しました。IJGライブラリを用いて生成しただけのシンプルなJPEGファイルでも転送できています。このファイルにはExif情報は全く含まれていませんが、Eye-Fiカードもそこまではチェックしていないようです。

Eye-Fi対応カメラであれば画像転送中であることの判別ができるようなので、できればその方法が知りたいところです。しかしながら、現状ではQVGA画像を圧縮しているため、ファイルサイズは20KB~30KB程度でしかなく、転送にも時間を要しません。撮影後2分も待てば無線LANを見つけて転送を完了できるでしょうから、カードへの電源供給を断ってもいいかと思われます。

Eye-Fiは16KB以下の画像を転送できない?

2012-05-12 19:01:13 | Weblog
カメラからの画像をJPEGファイルを書き込めるようになったので、Eye-Fiカードを使うことで自動アップロードの実験中です。

Eye-Fiカードは、カードに書かれたどんなファイルでも転送できるわけではありません。写真であれば、DCF規格に沿って作成されたカメラ画像を転送するようになっているようです。例えば、ルートディレクトリに書き込まれたJPEGファイルは転送されません。DCF規格が定めるディレクトリ構造配下にあるJPEGファイルでないと転送しません。ファイル名にも規則がありますので、これに従った名前を付けてやる必要があります。

このように、DCF規格に沿った構造、名前を付けてカードへJPEGファイルを書き込むことで、アップロードができるようになりました。ところが、撮影したファイルの一部しかアップロードできていません。連番でファイル名を付けているにもかかわらず、転送されるファイルもあれば、転送されないファイルもあります。どのファイルも、PC上では通常のJPEGファイルとして見ることができるのですが、Eye-Fiが確認する条件を満たさないものがあるようです。きょう試した画像を調べたことろではどうやらファイルサイズが16Kバイトに満たないものが転送されていないようです。あくまでも想像ですが、16KB以下のファイルはデジタルカメラで撮影した画像としては小さすぎと判断されているのではないでしょうか?

撮影対象や条件によっても異なりますが、現在撮影したQVGA画像のサイズはおおむね15KBから25KB程度のサイズになっています。圧縮時のQualityパラメータを80に設定していますが、この値をもう少し大きくすることで画像品質を高めてやれば、出力されるファイルサイズも大きくなるでしょうから、この問題を回避できるかもしれません。

YCbCr出力をJPEG圧縮

2012-05-09 23:17:56 | CMOSカメラ
今度はカメラからの出力をYCbCrに切り替えて、その出力をJPEG圧縮しました。昨夜に続いてちょっと明るめの部屋で撮りましたが、RGB565出力の時と比べて、色が綺麗にでています。これなら満足できますね。



JPEG圧縮にはIJGのライブラリを使っていますが、QVGAの画像を圧縮するのにおよそ30KBのメモリを使用しています。このあたりの事情については、また改めて記事を書く事にします。

時刻を埋め込む

2012-05-08 22:53:35 | CMOSカメラ
SAM3Sになってありがたいことのひとつが、RTC機能が備わっていること。外付けでRTCを付けるスペースと費用を節約できます。ちょっと残念なのは、RTCのバックアップ用電源端子が無い事。RTCだけを動かすにはバックアップ電源の切り替えを用意したうえで、Deep sleepモードへ移行して、必要な時にWakeupするようにソフトを組んでやらねばなりません。

当面はRTCを使うだけにして、電池駆動や低消費電力モードへの移行処理は後で機能追加することにしておきます。RTCの使用目的は、カメラから取得した画像に撮影時刻を埋め込むこと。そしてSDカードへの書き込み時のタイムスタンプです。後ほど、アラーム機能を使って定期的な自動撮影機能も追加していくつもりです。まずは、画像に日付と時刻を入れました。



うーん、RGB565から24bit BMPファイルを作成しているために偽色が出ているかもしれませんね。カメラ出力をYCbCrに切り替えてJPEGファイルを生成すべく、作業中です。