Image Acquisition Toolboxを用い​て顕微鏡のカメラから​画像を1分間隔で取得​し,pc内に保存する​方法について

こちらのページ(https://jp.mathworks.com/help/imaq/acquiring-a-single-image-in-a-loop.html)を参考に,Image Acquisition Toolboxを用いて顕微鏡のカメラから画像を1分間隔で取得し,pc内に保存しようと試みたのですが画像が1枚しか取れませんでした.
vid = videoinput('winvideo', 1, 'RGB8_2752x2192');
preview(vid);
for i = 1:20
snapshot = getsnapshot(vid);
imagesc(snapshot);
end
顕微鏡から長時間(18~27時間程度),1分ごとに1枚画像を取得し続けるのはどのように行えばいいのでしょうか.
加えて,撮影した画像を外部アプリケーション(imagJ)などで動画化したいので,PCの内部メモリに保存したいです.
どなたかお力添えをいただければと思います.
よろしくお願いいたします.

 Accepted Answer

こんにちは.
ぱっと思いつくのは,video input オブジェクトのタイマー機能を使用する方法でしょうか.
video input オブジェクトには,様々なタイミング(トリガー)で画像を取得するための機能が実装されています.今回は,次のような設定を行います.
  • マニュアルでトリガーをかける
  • トリガー1回につき画像を1枚取得
  • タイマー機能を使って,一定時間経過毎に「マニュアルトリガーを発生する関数(下記では mySnapshot)」を起動する.
vid = videoinput('winvideo',2,'MJPG_1024x576');
triggerconfig(vid,'manual');
vid.FramesPerTrigger = 1;
vid.TriggerRepeat = 5; % トリガ繰り返し数.この値を Inf に設定すると無限ループで画像を取得できます.
vid.TimerPeriod = 5; % 繰り返し周期を秒で指定します.1分間隔なら 60 です.
vid.TimerFcn = @mySnapshot;
また,データをメモリ上に保持されたいとの事でしたので,そのための仕掛けを仕込みます.やり方は色々ありますが,ここでは video input オブジェクトの UserData プロパティを使います.具体的には,フィールド imagescounts を持つ構造体を UserData とします.
% ビデオオブジェクトの UserData プロパティに画像データ保存用のバッファを設定
maxImageNumber = 10;
imageBuffer.images = repmat(uint8(0),[576 1024 3 maxImageNumber]);
imageBuffer.counts = 0;
imageBuffer.size = size(imageBuffer.images,4);
vid.UserData = imageBuffer;
ここまで準備したところで video input オブジェクトを走らせると,設定時間間隔で画像を取得し,UserData プロパティに保存してくれます.
start(vid);
trigger を既定の回数受け付けたら video input オブジェクトは自動的に停止します.TriggerRepeat の値を Inf に設定した場合には,マニュアルで停止する必要があります.
stop(vid);
記録されている画像を確認しましょう.どういうわけだか,vid.UserData.images といったドット表記の連鎖が上手く使えない(エラーになる)ので,getfield でバッファの内容を取得しています.
numFrames = vid.FramesAcquired;
imgData = getfield(vid.UserData,'images');
imgData = imgData(:,:,:,1:numFrames);
imshow(imtile(imgData));
関数 mySnapshot はこちら.
function mySnapshot(vid,~)
% トリガーをかけて画像を取得します.
trigger(vid);
img = getdata(vid,1);
% 画像バッファーを取得,カウンタをインクリメントして画像を保存します.
buffer = vid.UserData;
buffer.counts = buffer.counts + 1;
if buffer.counts <= buffer.size
buffer.images(:,:,:,buffer.counts) = img;
end
% ビデオオブジェクトの UserData を更新
vid.UserData = buffer;
end

7 Comments

回答していただきありがとうございます.
教えていただいたようにコードを打ち込み,実行してみましたが,
関数 'mySnapshot' (タイプ'videoinput' の入力引数) が未定義です。
警告: The TimerFcn callback is being disabled.
To enable the callback, set the TimerFcn property.
と表示され,撮影することができませんでした.
お手数をおかけしますが,こちらについても解決方法を教えていただければと思います...
何卒よろしくお願いいたします.
vid = videoinput('winvideo', 1, 'RGB16_2752x2192');
triggerconfig(vid,'manual');
vid.FramesPerTrigger = 1;
% トリガ繰り返し数.この値を Inf に設定すると無限ループで画像を取得できます.
vid.TriggerRepeat = 5;
% 繰り返し周期を秒で指定します.1分間隔なら 60 です.
vid.TimerPeriod = 5;
vid.TimerFcn = @mySnapshot;
% ビデオオブジェクトの UserData プロパティに画像データ保存用のバッファを設定
maxImageNumber = 10;
imageBuffer.images = repmat(uint8(0),[576 1024 3 maxImageNumber]);
imageBuffer.counts = 0;
imageBuffer.size = size(imageBuffer.images,4);
vid.UserData = imageBuffer;
start(vid);
preview(vid);
言葉足らずですみませんでした.
コード例を打ち込みながら実行するためには,関数 mySnapshot は,別ファイル mySnapshot.m として別途保存する必要があります.あるいは,コード例全体をひとつの mファイル(スクリプト)として保存して実行することもできます.(この場合は mySnapshot はローカル関数となります.)
ご参考までに,後者のサンプルを添付します.
サンプルを添付していただきありがとうございました.
実行することが出来たのですが,撮影されたデータを見つけることができませんでした...
userdata propertyに保存されるとのことですが,こちらはどこから確認すればよろしいのでしょうか?
何から何まで質問してしまい,本当に申し訳ありません.
何卒よろしくお願いいたします.
撮影されたデータは,video input オブジェクトの UserData プロパティに格納されているので,次の方法で取り出すことができます.imgData は4次元の配列で,各次元が「画像高さ,画像幅,RGB,撮影順」に対応しています.
imgData = getfield(vid.UserData,'images');
と,ここまで書いてふと思ったのですが,ひょっとして,画像をメモリではなくてファイルに保存したいということでしたでしょうか? それでしたら,関数 mySnapshot に,イメージファイル書き出し関数 imwrite を加えることで実現できます.実行すると,撮影された画像を逐次画像ファイルとしてカレントディレクトリに書き出します.書き出すファイル形式なども指定できますので,詳細は imwrite のドキュメンテーションを参照してください.
function mySnapshot(vid,~)
% トリガーをかけて画像を取得します.
trigger(vid);
img = getdata(vid,1);
% 画像バッファーを取得,カウンタをインクリメントして画像を保存します.
buffer = vid.UserData;
buffer.counts = buffer.counts + 1;
if buffer.counts <= buffer.size
buffer.images(:,:,:,buffer.counts) = img;
end
% 適当にファイル名を設定してイメージをファイルに書き出し
filename = sprintf('test_%04d.jpg',buffer.counts);
imwrite(img, filename);
% ビデオオブジェクトの UserData を更新
vid.UserData = buffer;
end
また,後工程で動画を作成されていとのことですが,画像ファイルを保存する代わりに,直接動画ファイルを出力する事もできます.その場合には,VideoWriter オブジェクトを使用する事になります.(最初の回答に付記した参照ページの内容がまさにそのものです.コーディング例を添付しますね.)
添付していただいたものを使わせていただいて,動画まで撮影することができました.
後工程で動画を作成しようとしたのは理由がありまして,以前にも他の方にアドバイスいただいて以下のようなコードを作成したのですが,実験用に使っている顕微鏡のカメラでのみ,撮影した際に録画が途中で止まってしまうということがありました.(webカメラ等では正常に録画できました.)
% Webcamera から直接AVIに保存する
% Video Inputオブジェクトの作成
vid = videoinput('winvideo', 1, 'RGB16_2752x2192');
% Video Inputオブジェクトの作成
src = getselectedsource(vid);
% フレームレートを 2 Frame/Sec に変更(RGB16_2752x2192(LuCam)では使えない)
% set(src,'FrameRate','2');
% 撮影領域の変更
%vid.ROIPosition = [409 1043 1280 720];
% 取得するフレーム数
vid.FramesPerTrigger = 660;
% 間引き数 (60フレームごと / 60fps の場合 1sec 毎に取得)
% RGB16_2752x2192のカメラでは60フレームごと→30秒に1枚撮影
% RGB16_2752x2192のカメラでは120フレームごと→60秒に1枚撮影
vid.FrameGrabInterval =120;
% タイムアウト設定 (取得する時間以上の時間を設定)
vid.Timeout = 670;
% ロギングする対象 (disk / memory?)
vid.LoggingMode = 'disk';
diskLogger = VideoWriter('210907 LuCam test.avi');
vid.DiskLogger = diskLogger;
vid.ReturnedColorspace = 'rgb';
% 画像取得開始
start(vid);
preview(vid);
ですので,MATLABで動画を作ることでPCに負荷がかかりすぎているのかと考え,今回,毎分画像だけ撮るようにしたいと思い,質問させいていただいた次第です.
2021 0908 23:22追記
添付していただいたものを使い撮影していたところ,以下のエラーが発生しました.
Error event occurred at 22:19:33 for video input object: RGB16_2752x2192-winvideo-1.
GETDATA timed out before FRAMES were available.
おそらくこのエラーがいままでも録画がとまってしまう原因となっていたと思いますが,何か解決方法はありますでしょうか?
また,出力される動画が1秒に1枚ずつ再生されており,撮影枚数=動画の時間になっていますが,ここを短縮することは可能でしょうか?
重ね重ね大変申し訳ないのですがよろしくお願いいたします.
こんにちは.
まず,出力される動画の再生レートについては,ビデオライターオブジェクトのプロパティを設定することで変更できます.先に添付したmファイルの19行目がそれで,
vwObj.FrameRate = 1;
となっており,1秒に1フレーム再生する設定になっています.ここを次のように変更すると 30フレーム/秒 で再生するようになります.
vwObj.FrameRate = 30;
getdata 関数のタイムアウトの問題は少々手強いです.MATLAB Answers にもいくつか似たような事例が挙がっていて,決定的な解決策はなさそうです.
かなり古いエントリ(英語)ですが,
に書かれている対処方を試していただいて,解決できないようでしたらMathWorks のテクニカルサポートに連絡されることをお勧めします.その際に,お使いのカメラの型番,ファームウェアのバージョン,接続形態(USB,Ethernet, ... )などの情報をお伝えいただけるとスムーズに話が進むと思います.
タイムアウトの対処法の方,確認させていただきました.
1つ目のタイムアウトまでの時間を大幅に延長して行いましたが,やはりエラーが出てしましました.
2つ目の方法を試し,それでも解決しないようであればテクニカルサポートに問い合わせさせていただこうと思います.
getdataの前ということは以下のようなことでよろしいでしょうか?
function mySnapshot(vid,~)
% トリガーをかけて画像を取得します.
trigger(vid);
% 2つ目の方法の追加箇所
while get(vid,'FramesAvailable')<1 %Wait until at least 1 frame is available
unavailable=1;
end
frame = getdata(vid,1);
vwObj = vid.UserData;
writeVideo(vwObj,frame);
end

Sign in to comment.

More Answers (0)

Products

Release

R2021a

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!