質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.46%
C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

Linux

Linuxは、Unixをベースにして開発されたオペレーティングシステムです。日本では「リナックス」と呼ばれています。 主にWebサーバやDNSサーバ、イントラネットなどのサーバ用OSとして利用されています。 上位500のスーパーコンピュータの90%以上はLinuxを使用しています。 携帯端末用のプラットフォームAndroidは、Linuxカーネル上に構築されています。

Raspberry Pi

Raspberry Piは、ラズベリーパイ財団が開発した、名刺サイズのLinuxコンピュータです。 学校で基本的なコンピュータ科学の教育を促進することを意図しています。

Q&A

解決済

2回答

1990閲覧

【C言語】ラズパイ接続カメラで撮影した連写画像(RAWデータ)の撮影時刻(ミリ秒単位)のデータを取得したい

Uka

総合スコア28

C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

Linux

Linuxは、Unixをベースにして開発されたオペレーティングシステムです。日本では「リナックス」と呼ばれています。 主にWebサーバやDNSサーバ、イントラネットなどのサーバ用OSとして利用されています。 上位500のスーパーコンピュータの90%以上はLinuxを使用しています。 携帯端末用のプラットフォームAndroidは、Linuxカーネル上に構築されています。

Raspberry Pi

Raspberry Piは、ラズベリーパイ財団が開発した、名刺サイズのLinuxコンピュータです。 学校で基本的なコンピュータ科学の教育を促進することを意図しています。

1グッド

0クリップ

投稿2021/11/14 15:39

編集2021/11/20 09:42

前提・実現したいこと

ラズパイ4に搭載のカメラで撮影した連写画像(RAWデータ)の撮影時間(ミリ秒単位)を取得したいと考えています。時間を取得する目的は、物体の落下挙動を高速で連写撮影し、各画像における物体の移動距離と時間から落下加速度を計算したいためです。下記のコマンドで1秒間に約200枚のRAW画像が取得しています(00001.RAWから連番で取得)。各画像の撮影時刻または経過時間を記録できるような方法がありましたらご教示いただけますとありがたいです。よろしくお願いします。

※Raspberry pi4使用
※カメラ情報
グローバルシャッタータイプカメラ
https://www.inno-maker.com/product/cam-mipiov9281/

※2021.11.19 試したことを追記。
撮影時刻(ファイルの保存時刻ではなく)を確認するには、下記のリンクに書いてあるようなC言語プログラムが必要とのアドバイスをいただきました。v4l2_bufferに撮影時間に関するタイムスタンプ情報が格納されているようですが、具体的にどのようにデータを操作すればいいのかわかりません。わかる方がいましたら教えていただけるとありがたいです。
https://stackoverflow.com/questions/10266451/where-does-v4l2-buffer-timestamp-value-starts-counting

該当のソースコード

timeout 1 ./v4l2_capture_y8

試したこと

回答者様からアドバイスをいただき、下記コマンドで各画像のタイムスタンプを取得しました。
ls --full-time -i | sort -u

結果として、連番のファイルに対して、同じタイプスタンプが2連続ないし3連続で記録されています。(結果を一部抜粋)
イメージ説明
200fpsでの高速撮影を実現するために、カメラの仕様として、2ないし3連写してからデータを一気にメモリに転送してファイル保存する(だからファイル保存時刻としては同じタイプスタンプが並ぶのではないか?)ようなことをしているのではないかと思っています。

この結果を用いて加速度を求めてみました。
方法:連続しているタイムスタンプ(x個並んでいるとして)については、これらのタイムスタンプの前後のタイムスタンプの差分をxで割り算して時間⊿を求め、前のタイムスタンプ+Δiを推定タイムスタンプとして使用。
結果:落下加速度は、理論値(9.806 65m/s2)に近い値になっているものもあれば、大きく外れる、あるいはマイナス値になるものもありました。おそらく、補正した後のそれぞれのタイムスタンプ(+Δi)の値が微妙に違っているのではないかと思いますす。

退会済みユーザー👍を押しています

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2021/11/14 21:13

https://stackoverflow.com/questions/10266451/where-does-v4l2-buffer-timestamp-value-starts-counting バッファにタイムスタンプ情報が格納されているらしいです。 紹介されているコードだと、これを実現するにはCが必要そうです。 なので、Cのタグをつけると詳しい人のフォローが得られると思います。 か、ファイルが保存されているタイムスタンプを見て、「つじつまが合っている」のであれば 保存時刻タイムスタンプをファイル名にするのもありかも? 参考:Pythonで似たようなこともできますが、遅すぎてfpsが1/10くらいまで落ちるはずです。
Uka

2021/11/15 10:44

>fourteenlengthさん アドバイスいただきありがとうございます。いただいたリンク見てみました、具体的にどのように実装すればいいのか、自分でも調べてみたいと思います。 >ファイルが保存されているタイムスタンプを見て、「つじつまが合っている」のであれば 保存時刻タイムスタンプをファイル名にするのもありかも? ファイルのタイムスタンプはプロパティから確認する感じでしょうか??それだと秒までしかわからないので、ミリ秒までは確認できないかなと思っていました。
Uka

2021/11/15 14:09 編集

調べていただきありがとうございます。早速こちらのコマンドを試してみたところ、同じタイムスタンプのファイルが2連続ないし3連続して取得されました。知りたいのは各画像の撮影時刻ですが、これがファイルとして保存されるタイムスタンプと一致すると考えていいでしょうか?今回の結果からすると、もしかしたら2連写くらいしてデータをためてから一気に転送してファイル保存する(だから同じタイプスタンプが並ぶ?)というようなことをしているのかなと思いました。
退会済みユーザー

退会済みユーザー

2021/11/15 21:05 編集

多分高速化するためにそういうような処理はされていると思います。本当はどうか調べようとするとソースコードを見ないといけません。 適当なところで妥協をするのであれば、 1. ファイル一覧とタイムスタンプ一覧をコマンドラインからCSVか何かに保存 2. タイムスタンプが同じものを検出 3. 次のタイムスタンプまでの時間Δを計算 4. Δを同じタイムスタンプの数で割りΔiを算出 5. 前のタイムスタンプ+Δiを推定タイムスタンプとして使用 でしょうか。
Uka

2021/11/16 15:41 編集

アドバイスいただきありがとうございます、おっしゃる方法で重複しているタイムスタンプについて推定タイムスタンプを算出し加速度を計算してみましたが、同じタイムスタンプで記録されていても実際には撮影時間(⊿i)が微妙に違っているのか、理論値(9.806 65m/s2)に近いものもあれば、大きく外れる、あるいはマイナス値になるものもありました。他の対象物でも検証してみたいと思います。
退会済みユーザー

退会済みユーザー

2021/11/16 21:08

だとすると、多分ちゃんとタイムスタンプを取るにはCを使うしかないと思います。 詳しく目が肥えた人に答えを書いてもらえるように、質問の書き方を直した方がイイです。 「発生している問題・エラーメッセージ」は不要ですし、これまでやってきたこと、何が良くて何がダメだったか、文章を再構成することはもちろん、見た目的にもインデックスを付けたり装飾を付けたりして見やすく回答をもらいやすくした方がイイです。 「細かいことは全部コメント欄を見てくれ」だと多分回答はもらえませんので…
Uka

2021/11/19 12:54

>fourteenlengthさん アドバイスいただいていたにもかかわらずお返事できずに申し訳ありませんでした。正確なタイムスタンプを取得するには、やはり前回教えていただいたC言語を使わないといけないということですね。詳しい方からアドバイスもらえるように質問を修正したいと思います、いつも貴重なアドバイスありがとうございます。
jimbe

2021/11/20 08:35

ラズパイもカメラも分かりませんが。 お使いの v4l2_capture_y8 コマンドのソースコードはどこで見られるでしょうか。
Uka

2021/11/20 09:53 編集

jimbeさん コメントいただきありがとうございます。必要な情報が抜けており申し訳ありません、追記致しました。v4l2_capture_y8はファイルとしてメーカーから提供されているものですが(初期設定のときにインストールしています)ファイルを開くことができないので、ソースコードを確認できません。このソースがわからないとバッファに格納されているタイムスタンプ情報を確認することは難しいでしょうか、、?
dodox86

2021/11/20 10:04

ファイルとして保存した日時、すなわちファイルのタイムスタンプだと、linuxでのファイルシステムへの保存タイミングのタイムラグが問題になりそうです。linuxだと書き込みのバッファリングはしているはずですが、ラズパイで使うSDカードは書き込みも遅く、撮影日時とファイルの保存日時の正確な一致はちょっと望めないのではないかなと思うのですが。
dodox86

2021/11/20 13:46

そもそもの話として、質問者さんはC言語は分からないと言うことなのでしょうか。分かるのであればソースコードを入手して、該当の部分を追加、修正すれば良い話なはずなのですが。いわゆるプロプライエタリな製品のコードで、情報が公開されている部分が少ない様であればおのずとアプローチの方法が変わります。
Uka

2021/11/21 10:16

>dodox86さん アドバイスいただきありがとうございます。C言語は基本的な文法は理解しています。おっしゃるように、Githubからコードを確認できるようなので、他の回答者様からのアドバイスも参考しながら該当の箇所を修正して試してみたいと思います。
guest

回答2

0

ベストアンサー

cam_get_image のローカルで用意している struct v4l2_buffer buffer を外から渡すようにして、そのタイムスタンプをファイル名を作っている sprintf で使うと良さそうですね。


以下のような感じです。

※コンパイルもしていません!!

main 書き換え(cam_get_image に struct v4l2_buffer をパラメータとして渡し、中の情報でファイル名を構成する)

c

1() 2 while (1) { 3 int count = 0; 4 for(j=0;j<optcapcnt;j++) 5 { 6 struct v4l2_buffer buffer; //★追加 7 ret = cam_get_image(buf, IMAGE_SIZE, &buffer); //★パラメータ追加 8 //ret = cam_get_image(buf, IMAGE_SIZE); 9 ASSERT(ret==0); 10 11 char tmp[64] = {"---\n"}; 12 for (i=0; i<16; i++) 13 sprintf(&tmp[strlen(tmp)], "%02x ", buf[i]); 14 LOGD("%s", tmp); 15 16 char filename[32]; 17 double temp_ms = 1000 * buffer.timestamp.tv_sec + (buffer.timestamp.tv_usec / 1000.0); //★追加 18 sprintf(filename, "./%05d_%.2f.raw", count++, temp_ms); //★ファイル名に時間を追加 19 //sprintf(filename, "/sdcard/%05d.raw", count++); 20 //sprintf(filename, "./%05d.raw", count++); //★コメント化 21 //sprintf(filename, "./%05d.raw", count); 22 int fd = open(filename,O_WRONLY|O_CREAT,00700);//保存?像数据 23()

cam_get_image 書き換え(struct v4l2_buffer をパラメータとして受け取る)

c

1//int cam_get_image(u8* out_buffer, int out_buffer_size) 2int cam_get_image(u8* out_buffer, int out_buffer_size, struct v4l2_buffer *buffer) //★ 3{ 4 int ret; 5 //struct v4l2_buffer buffer; //★ 6 7 memset(buffer, 0, sizeof(struct v4l2_buffer)); //★ 8 buffer->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; //★ 9 buffer->memory = V4L2_MEMORY_MMAP; //★ 10 buffer->index = BUFFER_COUNT; //★ 11 ret = ioctl(cam_fd, VIDIOC_DQBUF, buffer); //★ 12 if (ret != 0) 13 { 14 DBG("ioctl(VIDIOC_DQBUF) failed %d(%s)", errno, strerror(errno)); 15 return ret; 16 } 17 18 if (buffer->index < 0 || buffer->index >= BUFFER_COUNT) //★ 19 { 20 DBG("invalid buffer index: %d", buffer->index); //★ 21 return ret; 22 } 23 24 DBG("dequeue done, index: %d", buffer->index); //★ 25 memcpy(out_buffer, video_buffer_ptr[buffer->index], IMAGE_SIZE); //★ 26 DBG("copy done."); 27 28 ret = ioctl(cam_fd, VIDIOC_QBUF, buffer); //★ 29 clock_gettime(CLOCK_REALTIME, (struct timespec *)&buffer->timestamp); //☆ 30 buffer->timestamp.tv_usec /= 1000; //ナノ->マイクロ補正 31 if (ret != 0) 32 { 33 DBG("ioctl(VIDIOC_QBUF) failed %d(%s)", errno, strerror(errno)); 34 return ret; 35 } 36 DBG("enqueue done."); 37 38 return 0; 39}

投稿2021/11/20 13:02

編集2022/01/06 15:29
jimbe

総合スコア12756

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Uka

2021/12/29 13:18

>jimbeさん ご回答くださいましてありがとうございます。struct構造体についてあまり理解できておらず、申し訳ござません。main文のwhile(1)のなかに下記★のコードを追加して実行してみましたが、エラーがでてしまいました。ret = cam_get_image(buf, IMAGE_SIZE);の引数で使っている変数bufとstruct構造体で使っているbufがかぶっているのが原因でしょうか?初歩的な質問で申し訳ありませんが、ご教示いただけたらありがたいです。 char filename[32]; //sprintf(filename, "/sdcard/%05d.raw", count++); struct v4l2_buffer buf; //★追加 long temp_ms = 1000 * buf.timestamp.tv_sec + (long) round( buf.timestamp.tv_usec / 1000.0); //★追加 sprintf(filename, "./%05d %g.raw", count++, temp_ms); //★ファイル名に時間を追加 //sprintf(filename, "./%05d.raw", count); int fd = open(filename,O_WRONLY|O_CREAT,00700);//保存图像数据 if (fd >= 0) { write(fd, buf, IMAGE_SIZE); close(fd); ****error**** v4l2_capture_y8.c:419:23: error: incompatible type for argument 2 of ‘write’ write(fd, buf, IMAGE_SIZE); ^~~ In file included from v4l2_capture_y8.c:2: /usr/include/unistd.h:366:45: note: expected ‘const void *’ but argument is of type ‘struct v4l2_buffer’ extern ssize_t write (int __fd, const void *__buf, size_t __n) __wur;
Uka

2022/01/01 11:52

コードご教示いただきありがとうございます。ポインタの使い方について勉強不足でまだ理解できない部分がありますが、いただいたコードで動作確認してみましたところ、ファイル名にはすべて同じ時間(1.09573e-6)がふられていました。2秒間の撮影で約350フレーム保存されているので、1フレームあたり5.7ミリ秒くらいの撮影時間になるはずですが、ファイル名にふられている時間はそれよりもかなり小さい値になっています。下記、時間を取得する部分については他のサイトから引用してきていますが、setupに記載されている部分を追加してなかったので、それが原因でしょうか?また、できればある起点(たとえば、撮影前のある時間)からの経時時間として各フレームの撮影時間がわかるようなコードにしたいと考えています。アドバイスいただけるようでしたら大変ありがたいです。 long temp_ms = 1000 * buffer.timestamp.tv_sec + (long) round(buffer.timestamp.tv_usec / 1000.0); //★追加 引用先: https://stackoverflow.com/questions/10266451/where-does-v4l2-buffer-timestamp-value-starts-counting
jimbe

2022/01/01 12:21

ファイル名を生成する部分はご提示頂いた計算やフォーマットで正しいのだろうという前提で、cam_get_image から buffer を外に出すことを主体にコードをご提示致しました。 が、よく見るとファイル名へのフォーマット文字列が間違っています。 > sprintf(filename, "./%05d_%g.raw", count++, temp_ms); //★ファイル名に時間を追加 において、 "%g" は float 用です。 temp_ms は long ですので "%ld" 等としないといけないですね。 フォーマット文字列を "./%05d_%ld.raw" としたらどうなるでしょうか。 v4l2_buffer 構造体に入っているタイムスタンプがどういう性質のものなのかは、開発環境どころか実機を触ったことも無い私には何とも言えません。 引用先のコードは、単に epoch 時間との差を求めておいて、表示時に epoch 時間にしているだけですので、クロック自体には関係無いものと思います。
Uka

2022/01/01 16:07 編集

アドバイスありがとうございます。出力フォーマットを%ldに修正してみましたが、今度はすべてのファイル名で"0"と出力されるようになりました。 下記リンクの、struct v4l2_bufferに関するドキュメンテーションのなかで、3.6.1. struct v4l2_bufferに、struct timeval、timestampの項目とV4L2_BUF_FLAG_TIMESTAMP_*や clock_gettime()関数についての記述があり、これらをコードのなかであらかじめ定義しておく必要はないでしょうか?カメラ内部に関する質問になってしまうかもしれませんが、もしわかることがあればアドバイスいただたらありがたいです。 https://www.kernel.org/doc/html/v4.9/media/uapi/v4l/buffer.html
jimbe

2022/01/01 18:09

timestamp が実時間だったら 0 ということは無さそうですが、 clock だとどう入っているのか微妙に分からない感じですね。 いっそ temp_ms の代わりに buf.timestamp.tv_usec を直に使って、まず値が入っているか試したほうが良いのかもしれません。 sprintf(filename, "./%05d_%ld.raw", count++, buf.timestamp.tv_usec); バッファフラグの説明の中では、 V4L2_BUF_FLAG_TIMESTAMP_MASK と V4L2_BUF_FLAG_TSTAMP_SRC_MASK の個所が timestamp に関係しているようですが、 buffer->flags は ( ゼロクリア後 ) 設定していませんので V4L2_BUF_FLAG_TIMESTAMP_UNKNOWN と V4L2_BUF_FLAG_TSTAMP_SRC_EOF になり、 値の違いはあっても 0 になることは無さそうに見えます。 V4L2_BUF_FLAG_TIMECODE のような有効/無効フラグは見当たりませんし。 V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC や V4L2_BUF_FLAG_TIMESTAMP_COPY 、 V4L2_BUF_FLAG_TSTAMP_SRC_SOE を試しても良いかもしれません。 ( V4L2_BUF_FLAG_TIMESTAMP_COPY は mem2mem デバイスのみらしいですけど、本件は当たるのでしょうか。)
Uka

2022/01/03 11:36 編集

詳細にアドバイスいただきありがとうございます。buf.timestamp.tv_usecをsprintの中に直接入れて試してみましたが、結果は変わらずすべて0で出力されました。結果だけみると、時間を取得できていないように見受けられます。 >V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC や V4L2_BUF_FLAG_TIMESTAMP_COPY 、 V4L2_BUF_FLAG_TSTAMP_SRC_SOE を試しても良いかもしれません。 とのことですが、たとえばV4L2_BUF_FLAG_TIMESTAMP_MONOTONICを使って確認したい場合は追加でどのように記述すればいいでしょうか?ポインタや構造体についてまだ理解しきれてなく、ご教示いただけるとありがたいです。 または、bufferに格納されているtimestampを取得することが難しいようであれば、次善策として、clock_gettime()関数をint cam_get_imageのなかで呼び出して(たとえばret = ioctl(cam_fd, VIDIOC_DQBUF, buffer)の直後に呼び出すなど)、mainのwhile(1)のなかで戻り値として返すことは可能でしょうか? たびたびお伺いして申し訳ありませんが、アドバイスいただけると大変ありがたいです。
jimbe

2022/01/03 12:01 編集

buf.timestamp.tv_usec に値が無いとなると、暗雲立ち込める感じですねぇ。バッファの設定の他にも(カーネル的設定以外でもドライバレベル? とかハードレベル、マイクロコードレベルとか)何かあるのか…。 V4L2_BUF_FLAG_* (MASK 以外 ) は buffer->flags に代入する値です。 cam_get_image 関数で buffer->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; //★ buffer->flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC | V4L2_BUF_FLAG_TSTAMP_SRC_SOE; //☆ buffer->memory = V4L2_MEMORY_MMAP; //★ のように ☆ の行(表示上 2 行に見えるかもしれませんが 1 行です)を追加する感じです。 ビットの値ですので、複数を指定する時はビット演算子の OR("|") で並べます。 タイムスタンプとして別途 clock の値を得ておいて返すというのであれば、また cam_get_image にパラメータを増やすよりも、 buffer->timestamp 構造体を利用してしまっても良いかもしれません。clock_gettime() の引数は struct timespec で struct v4l2_buffer の timestamp は struct timeval みたいですけども^^; ・・・同じなのでしょうか。
Uka

2022/01/03 12:14

早々にご返信ありがとうございます。 すみません、sprintに直打ちして出力するところで、 sprintf(filename, "./%05d_%ld.raw", count++, buf.timestamp.tv_usec); のコードをご教示いただきましたが、buffer.timestamp.tv_usecでよかったでしょうか?buf.timestamp.tv_usecにするとエラーがでたのでbuffer.timestamp.tv_usecにして実行していました。 cam_get_image 関数のなかに、 buffer->flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC | V4L2_BUF_FLAG_TSTAMP_SRC_SOE; //☆ を追加して試してみましたが、結果は変わりませんでした。 さきほどコメントさせていただきました、int cam_get_image()関数のなかにclock_gettime()関数を呼び出してmainに値を返すことはできるでしょうか?bufferでも同じ処理を行っているようで重複するかと思いますが、コードを直接追加することにより、時間を取得できるようでならその方法で試してみたいと思っています。
jimbe

2022/01/03 12:26

Oops! 2022/01/02 03:09 の私のコメントですね。すいません間違えています。buf と buffer がごちゃごちゃに^^;;; > buffer.timestamp.tv_usecでよかったでしょうか? そちらです、すいません。 コメント直し…たほうが良いのかどうか、コメントの進行的にビミョウ orz > 追加して試してみましたが、結果は変わりませんでした そうですか…。 Uka さんの案が現実的になってきました。 コメントに追加されていましたのでこちらも追加しましたが、行き違いになってますね^^; clock_gettime() の第二引数である struct timespec が struct v4l2_buffer の timestamp の型である struct timeval と同じであれば、 clock_gettime(clock_id, &buffer->timestamp); で入れられるかもしれません。(clock_id が何が良いのかは私には分かりません。)
Uka

2022/01/03 16:29

buffer.timestamp.tv_usecで問題ないとのことでよかったです、ありがとうございます。 clock_gettime()を利用する案についてもアドバイスありがとうございます。 while(1)のなかで、☆の箇所として追加して試してみましたが、->の部分がinvalidエラーとでてしまいました。おっしゃっていたように、clock_gettime()とstruct v4l2_bufferとで引数が違っていることが要因でしょうか?? struct v4l2_buffer buffer; //★追加 ret = cam_get_image(buf, IMAGE_SIZE, &buffer); //★パラメータ追加 long temp_s = clock_gettime(CLOCK_REALTIME, &buffer->timestamp); //☆
jimbe

2022/01/03 17:30 編集

clock_gettime(clock_id, &buffer->timestamp); を入れるのは、先にコメントで書いておられた > 次善策として、clock_gettime()関数をint cam_get_imageのなかで呼び出して(たとえばret = ioctl(cam_fd, VIDIOC_DQBUF, buffer)の直後に呼び出すなど) の場所の方ですね。 エラーとなりますのは、 main では buffer は ("struct v4l2_buffer buffer" と定義しているので ) 構造体そのものですが、 cam_get_image では buffer は ("struct v4l2_buffer *buffer" と定義しているので ) ポインタだからです。 この違いによって、 cam_get_image では構造体内のタイムスタンプのアドレスは &buffer->timestamp と書きますが main では &buffer.timestamp と書かなくてはならないため、「 (buffer はポインタじゃないんだから)"->" じゃなくない?」といってるのでしょう。 なお、 clock_gettime の戻り値は int で成功(0)か失敗(-1)かしか返さないようですので、戻り値は無視して良いかと思います。
jimbe

2022/01/04 03:24

回答の cam_get_image の方に ☆ として clock_gettime を入れてみました。 ただこの位置ですと、もしかすると後にある VIDIOC_QBUF の ioctl でさらに 0 で上書きされるかもしれませんので、もしこれでも 0 しか出ないようでしたら、 VIDIOC_QBUF の ioctl の下に移動することになるかもしれません。
jimbe

2022/01/04 03:30

v4l2_buffer のドキュメントでは timestamp は入ることになっているように思えますのに入らないのはどういうことかと思ってドライバのソースを探したのですが、公式サイトからドライバをダウンロードできる wiki に行っても流石に(?)ソースが見当たりませんでした。 何となく、『V4L2互換』を謳いながら実は処理が足りてないのでは…とコメントが漢文であることからも疑ってしまいます(--;
Uka

2022/01/04 14:55

>jimbeさん アドバイスと調査いただきありがとうございます。大変参考になります。 mainのなかにclock_gettime(clock_id, &buffer->timestamp)を入れた場合にエラーがでる理由について教えていただきありがとうございます。mainのなかではstruct構造体として(ポインタではなく)ふつうのbufferとして扱わなければならないためにエラーがでたものと理解しました。 あらためて、cam_get_image の方に ☆ として clock_gettime を入れて確認してみたところ、下記エラーがでました。これは、前回可能性としておっしゃっていた、clock_gettime()では引数の型がstruct timespec *だけど、struct v4l2_bufferでは引数の型がstruct timeval *でそれぞれ異なるためにでているエラーということですよね。 v4l2_capture_y8.c: In function ‘cam_get_image’: v4l2_capture_y8.c:178:35: warning: passing argument 2 of ‘clock_gettime’ from incompatible pointer type [-Wincompatible-pointer-types] clock_gettime(CLOCK_REALTIME, &buffer->timestamp); ^~~~~~~~~~~~~~~~~~ In file included from v4l2_capture_y8.c:13: /usr/include/time.h:219:66: note: expected ‘struct timespec *’ but argument is of type ‘struct timeval *’ extern int clock_gettime (clockid_t __clock_id, struct timespec *__tp) __THROW;
Uka

2022/01/04 15:15

>何となく、『V4L2互換』を謳いながら実は処理が足りてないのでは…とコメントが漢文であることからも疑ってしまいます(--; メーカからでているカメラのマニュアルにも、"Fully V4L2 ( Video4Linux) compatible device"とちゃんと記載されているんですが、、、インストールしたドライバに不備がある可能性があるということですね。 cam_get_image()のなかでclock_gettime()を使ってbufferを利用する方法が無理そうであれば、mainのなかでcam_get_image()の実行後にclock_gettime()を呼び出してフレームをキューにバッファリングしたあとの時間( ret = ioctl(cam_fd, VIDIOC_QBUF, &buffer);//缓冲帧放入队列)を取得する方法で妥協しようと思います。
jimbe

2022/01/04 17:07 編集

出たのはエラーでなくてワーニングじゃないでしょうか。つまりメッセージば出るけともコンパイルは出来ているのでは。 環境依存になりそうですが、どうも調べた感じでは 2つの構造体の構造は結局同じっぽいんですね。(long サイズのフィールドが2つ。) ですので、ワーニングメッセージが出てもコンパイルは出来ていたら、実行できるのではないでしょうか。 ワーニングが気になりましたら、キャストを入れてしまう手もあります。 clock_gettime(CLOCK_REALTIME, (struct timespec *)&buffer->timestamp); 『このアドレスは timespec* ですよ~』と言っちゃうんですね。 …とここまで書いてもう一度比較しましたら、 timespec の2つ目の long は(マイクロ秒で無く)ナノ秒じゃないですか。 timeval の仕様に合わせるなら clock_gettime の後で buffer->timestamp.tv_usec /= 1000; が必要そうです。
jimbe

2022/01/04 17:53

> mainのなかでcam_get_image()の実行後にclock_gettime()を呼び出して 何か、余計なことを書いた所為でぐるっと一周してしまって、 matukeso さんの回答をやっておけばという感じで申し訳ないです。
Uka

2022/01/06 13:40

warningの件、ありがとうございます。たしかに実行ファイルは出来上がっていました。よく確認しないで報告してしまい申し訳ありませんでした。キャストを入れたらwarningもなくなりました。 また、buffer->timestamp.tv_usec /= 1000; についてもご指摘ありがとうございます。usecについては構造体?で定義されているのだと思っていましたが、定義しないといけないんですね。 あらためて、ご指摘いただいた箇所を追記、修正したうえで実行してみましたが、結果は0となり変わりませんでした。。 >何か、余計なことを書いた所為でぐるっと一周してしまって、 matukeso さんの回答をやっておけばという感じで申し訳ないです。 こちらこそ、手を尽くしていただき本当にありがとうございました。bufferに保存されているなら、そこからタイムスタンプの情報を取得できればいいなと思っていたのと、構造体やポインタの使い方について知れることができたので大変勉強になりました。ありがとうございました。
jimbe

2022/01/06 13:55

> usecについては構造体?で定義されているのだと思っていましたが いえ、 tv_usec は定義されています。 1000で割る必要があるのは、 clock_gettime が扱う timespec では timeval の tv_usec に当たる部分が tv_nsec になっていて、ナノ秒が設定されるということです。 ですので buffer->tv_usec がマイクロ秒だと思って使うと実はナノ秒が入っていた…ということになってしまうので、 1000 で割ったほうが良さそうということです。 > 結果は0 ぇえ!?・・・ まさかとは思いますが、お使いのラズパイで clock_gettime が動いていないなんてことは・・・。 いやその前に、 2022/01/04 12:24 でコメントしましたように下の ioctl のほうで 0 で上書きしているかもしれませんので、☆の行を下の ioctl の下に移動させては如何でしょうか。
Uka

2022/01/06 14:39 編集

jimbeさん、勝手にクロージングしてしまいそうになり、すみませんでした(^^;) >tv_usec は定義されています。 1000で割る必要があるのは、 clock_gettime が扱う timespec では timeval の tv_usec に当たる部分が tv_nsec になっていて、ナノ秒が設定されるということです。 ですので buffer->tv_usec がマイクロ秒だと思って使うと実はナノ秒が入っていた…ということになってしまうので、 1000 で割ったほうが良さそうということです。 それぞれの構造体でタイムスタンプの単位が違うということですね、、気づいていただきありがとうございます! >いやその前に、 2022/01/04 12:24 でコメントしましたように下の ioctl のほうで 0 で上書きしているかもしれませんので、☆の行を下の ioctl の下に移動させては如何でしょうか。 移動させたらタイムスタンプが表示されるようになりました!!!本当にありがとうございます(泣) ファイル名としては、 00000_800420955, 00001_800420959,00001_800420964,.....,00348_800422674となっています。2秒間の撮影で約350フレームとれるので、平均5.7ミリ秒間隔で撮影できるはずなので、予想とも一致する時間になっているかと思います。 一点ご相談ですが、今回出力したタイムスタンプについて、少数点以下1or2桁くらいまで出力することはできるでしょうか?ためしに、.2%ldとして試してみましたが、結果は整数のままで変わりませんでした。(難しければナノ秒で出力するのもありかと思いますが、桁数が多くなるかと思うので...) 現状、出力箇所は以下のコードにしてあります。(オリジナルではlongのあとにroundが入っていましたが、これをいれるとエラーがでたので削除しました) long temp_ms = 1000 * buffer.timestamp.tv_sec + (long) (buffer.timestamp.tv_usec / 1000.0); //★追加 sprintf(filename, "./%05d_%ld.raw", count++, temp_ms); //★ファイル名に時間を追加
jimbe

2022/01/06 14:59

> それぞれの構造体でタイムスタンプの単位が違うということですね それです! どうも説明まで回りくどくなってスイマセン > 移動させたらタイムスタンプが表示されるようになりました いや長かったですね^^;;;;; temp_ms に小数点以下も入れるのでしたら、 long から double に変更して、割り算部分の long へのキャストを消すと良いかと思います。 double temp_ms = 1000 * buffer.timestamp.tv_sec + (buffer.timestamp.tv_usec / 1000.0); //★追加 その上で、 sprintf の "%ld" を double 用で必要な記述にすることになるでしょう。( "%.2f" ですかね。ファイル名に "." が二つ出ることになりそうですが、ラズパイは大丈夫でしょうか。)
jimbe

2022/01/06 15:23

ちなみに teratail は質問を (BA を選んで ) "解決済み" としても、コメントや回答、質問の編集まで BA を選ぶ前と変わらず出来ますので、いわゆる "クローズ" とはなりません。
Uka

2022/01/06 15:44 編集

早々にご返信いただきありがとうございます doubleへの変更で、小数点2桁まで出力できるようになりました!(拡張子のまえの"."と別々に出力されていました) いままでいろいろと親切に教えていただき本当にありがとうございました、当初のclock_gettime()を使わないでbufferからタイムスタンプを取り出す方法ができなかったのは残念ですが(メーカ側のドライバの不備?により)、やり方がわかって大変勉強になりました。ポインタや構造体については書籍で勉強中ですが、なかなかとっつきにくく、理解して使うにはまだ時間がかかりそうですが、今回の件でその重要性がよくわかりました。ありがとうございました。
Uka

2022/01/06 15:42

>teratail は質問を (BA を選んで ) "解決済み" としても、コメントや回答、質問の編集まで BA を選ぶ前と変わらず出来ますので、いわゆる "クローズ" とはなりません。 追記いただきありがとうございます、解決済にしたあとも質問できるんですね。 jimbeさんをベストアンサーとして解決済とさせていただきます!ありがとうございました。
jimbe

2022/01/06 16:35

おつかれさまでした。 解決後もイロイロ出来はしますが、サポートする/される場でもありませんのでご質問の範囲内で程々に…^^; 解決した後でもっと良い方法が見つかったのでそれを回答に書いておくとか、後から見直したら質問にこんな情報も付けて置いたほうが良かったとか、技術的な知識ベースとしてより良い情報にする為といった感じで利用されるのが良いかと思います。
guest

0

https://gitee.com/inno-maker/cam-mipiov9281/blob/master/tools/tools_code/v4l2_capture_y8.c
そのv4l2_capture_y8とやらのソースはあるんだから、openする前にsprintfしてるところでclock_gettimeでも呼んでファイル名に付与するよう改造すれば。

投稿2021/11/20 11:27

matukeso

総合スコア1601

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Uka

2021/11/24 14:34 編集

ソースコードが公開されていることを教えていただき、ありがとうございます。撮影時に呼び出しているプログラムを開いてソースコードを改変しようと思ったのですが、ファイルがバイナリ形式で保存されているようで、C言語で確認することができません(下記参照)バイナリ形式のファイルをC言語で開く方法がわかればいいのですが、それか教えていただいたリンクのCプログラムをラズパイに移植し.cの実行ファイルとして保存して、このソースコードを改変しても問題ないでしょうか? ----------------------- pi@raspberrypi:~/cam-mipiov9281/tools $ file -i v4l2_capture_y8 v4l2_capture_y8: application/x-executable; charset=binary 追記: 教えていただいたリンクのCプログラムをラズパイに移植し.cの実行ファイルとして保存し、試しにオリジナルのコードのまま、gccでファイルを実行してみましたが、エラーがでてしまいました。
dodox86

2021/11/25 01:21

@質問者 Ukaさん > 教えていただいたリンクのCプログラムをラズパイに移植し.cの実行ファイルとして保存し、試しにオリジナルのコードのまま、gccでファイルを実行してみましたが、エラーがでてしまいました。 .cの実行ファイル、gccでファイルを実行、と言う表現が分かりませんが、.cはC言語のソースファイルであり、ラズパイ用のgccでコンパイル~リンク(つまりビルド)して出来上がったものが実行ファイルですが、認識は合っていますか。
Uka

2021/11/25 14:50 編集

>dodox86さん コメントいただきありがとうございます。表現があいまいで申し訳ありません。 おっしゃるように、v4l2_capture_y8.c(Cプログラム)をラズパイのコマンドプロンプトからgccコマンドでコンパイルして実行ファイルを生成する過程で、下記のようなエラーがでてa.outの実行ファイルは作成されませんでした。 v4l2_capture_y8.c:432:11: error: expected declaration specifiers or ‘...’ before numeric constant Comment ( 0 )
dodox86

2021/11/25 15:33

ラズパイではなくWindows WSL/Ubuntu上のgccでコンパイルしたところ、そのファイルだけで実行ファイルのa.outは出来上がりました。 リンクで示されたソースファイル v4l2_capture_y8.c はファイルとして431行しかないはずですが、そのエラーメッセージは432行でコンパイルエラーを出しています。余分なテキストをコピー&ペーストしているんじゃないでしょうか。ちゃんとC言語のソースコードとして終わっているか確認しましょう。ちなみにそのページのソースコード外、Comment( 0 ) ....とか余分な部分を含めたら似たようなエラーが出ました。 "Comment ( 0 )" を末尾に含めてコンパイルした例: $ gcc -Wall t1.c t1.c:432:11: error: expected declaration specifiers or ‘...’ before numeric constant Comment ( 0 ) ^ 他回答者さんのコメント欄ですし、回答の範囲を超えているとも思うので私からのコメントはこの辺で。
Uka

2021/11/27 01:44

アドバイスありがとうございます。おっしゃるとおり、冒頭と最後に空白行があり、それらを削除することでコンパイルが通るようになりました。ありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.46%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問