MATLAB Answers

動画内の球体の速度を求めたい。

4 views (last 30 days)
水中を流れる球体(白と赤の2種類)があり、その球の速度を求めたいです。動画内にはスケールがあるので距離はわかります。また、120fpsとフレーム数が多いのでそれを利用できる方法があればな、と考えております。
現在は、イメージ内の円形オブジェクトの検出と測定(https://jp.mathworks.com/help/images/detect-and-measure-circular-objects-in-an-image.html)や、交通量のビデオ内の車の検出(https://jp.mathworks.com/help/images/detecting-cars-in-a-video-of-traffic.html)、複数オブジェクトの追跡(https://jp.mathworks.com/help/vision/ug/multiple-object-tracking.html)、カルマン フィルターを使用したオブジェクトの追跡(https://jp.mathworks.com/help/vision/examples/using-kalman-filter-for-object-tracking.html) などを参考に動画をインポートし、任意のフレーム(画像)を選択し、色のしきい値を調整することで(静止画内の)対象とする球を検出することまで出来ています。現在動画上で検出できるように挑戦しているところなのですが、動画内で追跡できたとしても速度を求める方法が見当たりません。
球の速度を求める方法(またはアイデア)を教えていただきたいです。
本ケースでは水中ということで背景も多少変化してしまうので、追跡等にも苦戦しています。
年内に完成させなければならず、行き当たりばったりなことができないので質問させていただきました。よろしくお願いいたします。
I=VideoReader('---.MP4')
%下式は動画Iの中の700フレーム目を画像として呼び出す
J=read(I,700);
imshow(J)
[BW,maskedRGBImage] = createMask1(J);
[centers,radii] = imfindcircles(BW,[15 28],'ObjectPolarity','bright','Sensitivity',0.91)
imshow(BW)
h = viscircles(centers,radii);
imshow(J)
h = viscircles(centers,radii);
以下、createMask1.m
function [BW,maskedRGBImage] = createMask(RGB)
I = rgb2lab(RGB);
channel1Min = 25.436;
channel1Max = 100.000;
channel2Min = 3.650;
channel2Max = 65.432;
channel3Min = 15.507;
channel3Max = 64.797;
% Create mask based on chosen histogram thresholds
sliderBW = (I(:,:,1) >= channel1Min ) & (I(:,:,1) <= channel1Max) & ...
(I(:,:,2) >= channel2Min ) & (I(:,:,2) <= channel2Max) & ...
(I(:,:,3) >= channel3Min ) & (I(:,:,3) <= channel3Max);
BW = sliderBW;
% Initialize output masked image based on input image.
maskedRGBImage = RGB;
% Set background pixels where BW is false to zero.
maskedRGBImage(repmat(~BW,[1 1 3])) = 0;
end

  2 Comments

Shunichi Kusano
Shunichi Kusano on 24 Dec 2019
シンプルに速度=距離÷時間の公式に則って計算するのではうまくいかなそうなのでしょうか。
Eiji Ishioka
Eiji Ishioka on 24 Dec 2019
ご回答ありがとうございます。
もちろん手動で距離と時間(or フレーム数)で速度を求めることはできますが、球の数、および動画数が多いのでmatlabで処理したいと考えました。
matlab上で同じことをするためによい方法はありませんでしょうか?
球を追跡し、一定区間(座標など)を通過するまでのフレーム数などで計算できないものかと考えていますが、役立つ関数などが見つかっておりません。
また、すでに同様の事例がありましたら参考にしたいと考え質問させていただいています。
よろしくお願いいたします。

Sign in to comment.

Accepted Answer

Shunichi Kusano
Shunichi Kusano on 25 Dec 2019
やりたいことがシンプルですので、特に関数も必要なく次の手順で実装するのがいいかと思います。
1.フレームごとに球の位置を取得
2.位置の変化から距離を計算
3.フレームレートからフレーム間の時間を計算
4.速度=距離÷時間で速度を計算
もし検出位置にばらつきがあるようでしたら、1フレーム毎ではなく複数フレームをまとめて計算すれば、安定した結果になるのではないかと思います。

  5 Comments

Show 2 older comments
Eiji Ishioka
Eiji Ishioka on 26 Dec 2019
ご回答ありがとうございます。
カルマンフィルタの説明最後まで読み切れてませんでした、、、createUtilitiesはとりあえず解決出来ました!ありがとうございます。
交通量調査の方で動画内で追うことは出来たのですが、画像一枚づつをとらえているだけなので複数の球が存在すると速度を求められないという問題があります。(もちろん人力で比較の組み合わせを見ればできることですが、あくまで自動で行いたいので、、、)
この点は交通量調査のやり方でも解決できるのでしょうか?(各フレームでのマーカーを同一物として認識させたいです。)
また、動作はしたものの途中で理解出来ない部分がありました。下記文のif~endまでが赤点のマーカーを出していることはわかるのですが各文の意味合い不明でした。
解説または参考になるものを教えていただきたいです。
Vid=VideoReader('')
J1=read(Vid,90);ーーー
imshow(J1)ーーー
[BW,maskedRGBImage] = createMask1(J1);
imshow(BW)
sedisk = strel('disk',12);
noSmallStructures = imopen(BW, sedisk);
d=imshow(noSmallStructures)
[centers1,radii1] = imfindcircles(noSmallStructures,[14 28],'ObjectPolarity','bright','Sensitivity',0.92)
imshow(noSmallStructures)
h = viscircles(centers1,radii1);
nframes = Vid.NumberOfFrames;
I = read(Vid, 1);
taggedCars = zeros([size(I,1) size(I,2) 3 nframes], class(I));
for k = 1 : nframes
singleFrame = read(Vid, k);
[BW,maskedRGBImage] = createMask1(singleFrame);
sedisk = strel('disk',10);
noSmallStructures = imopen(BW, sedisk);
d=imshow(noSmallStructures)
[centers1,radii1] = imfindcircles(noSmallStructures,[13 28],'ObjectPolarity','bright','Sensitivity',0.90)
h = viscircles(centers1,radii1);
taggedCars(:,:,:,k) = singleFrame;
stats = regionprops(noSmallStructures, {'Centroid','Area'});
if ~isempty([stats.Area])
areaArray = [stats.Area];
[junk,idx] = max(areaArray);
c = stats(idx).Centroid;
c = floor(fliplr(c));
width = 2;
row = c(1)-width:c(1)+width;
col = c(2)-width:c(2)+width;
taggedCars(row,col,1,k) = 255;
taggedCars(row,col,2,k) = 0;
taggedCars(row,col,3,k) = 0;
end
end
frameRate = Vid.FrameRate;
implay(taggedCars,frameRate);
初心者の質問ですみません。よろしくお願いいたします。
Shunichi Kusano
Shunichi Kusano on 27 Dec 2019
なるほど、複数あるんですね。その場合でしたら確かに検出だけではなく追跡が必要ですね。納得です。
ただ、この例ですと複数物体の追跡するには追記が必要になってしまいます。複数オブジェクトの追跡のサンプルが下記にあるので、こちらを参考にした方がいいのではないかと思います。
この例だと検出の原理が違う(foreground detection)ので、お手持ちのデータだとうまく動かないかもしれませんが、その場合は検出の部分だけ、今の色ベースのものに差し替えて使ってください。ちなみにこのサンプルも入れ子関数の構造になっていますので、少しわかりにくいです。
念のためif文の中についてざっと解説しておきます。
stats変数には検出できた車(と思われる)領域の面積(Area)と重心位置座標(Centroid)の情報が格納されています。
if文の中の処理は、isemptyで検出された車があれば実行されるよう条件付けされています。
処理の中身は、上から
  1. 一番面積の大きい領域の重心位置を取得(1-3行目)
  2. 重心位置(1点の情報)を5x5の領域に拡張(点だと面積1なので画像上で見づらいから広げているだけと思われます。fliplrは別にしなくてもいい。その場合はrowとcolが入れ替わる)(4-7行目)
  3. その領域を赤色(255,0,0)で塗りつぶす(taggedCarsは、読み込んだフレームに検出した車の重心位置を赤色でマーキングすることで検出結果を見やすく加工したものになる。)(残り)
となります。
Eiji Ishioka
Eiji Ishioka on 27 Dec 2019
お返事ありがとうございます。
動きに基づく複数オブジェクトの追跡ですが、背景が動かないことが前提だと思い、カルマンフィルタを使用したオブジェクトの追跡を参考にしておりました。検出部分の方法を入れ替えて挑戦してみたいと思います。
if文の中身を解説していただきありがとうございます。納得出来ました!
たくさん丁寧に教えていただきありがとうございます。

Sign in to comment.

More Answers (0)

Sign in to answer this question.