リアルタイムのカメラ​で目、口を検出したい​プログラムのエラーと​そのカメラ情報から三​角形の重心を求める

12 ビュー (過去 30 日間)
tsuyoshi tsunoda
tsuyoshi tsunoda 2021 年 8 月 30 日
コメント済み: tsuyoshi tsunoda 2021 年 8 月 31 日
教えていただきたい事は2つあります。
1つ目は、リアルタイムのカメラ映像のコードのエラーについてです。
以下のコードの22行目で、
フィールド名 "Eyes" が認識されません。
エラー: (行 22)
if or(app.Eyes.NumPts < 10 , app.Mouth.NumPts < 10)
というエラーが出てしまい、そちらの解決策を教えていただきたいです。
%カメラオブジェクト起動
app.cam = webcam();
%カメラからフレーム取得
VideoFrame = snapshot(app.cam);
%FPS設定
FPS = 20;
%タイマオブジェクト生成
app.UpdateTimer = timer('Period', 1/FPS,...
'ExecutionMode', 'fixedRate', ...
'TasksToExecute', Inf, ...
'TimerFcn', @app.mytimer_func);
%------------------------------------------------------------------
%タイマ割り込み関数 初期化関数で指定したFPSに応じて一定間隔で呼び出されます
%カメラからフレーム取得
VideoFrame = snapshot(app.cam);
%検出対象のオブジェクトポイント数がいずれか10以下になったら検出処理を行う
%オブジェクトポイント数が10より多い場合、フレーム毎検出領域のトラッキングを行う
if or(app.Eyes.NumPts < 10 , app.Mouth.NumPts < 10)
app.DetectingObjectsLamp.Enable = true;
app.TrackingObjectsLamp.Enable = false;
VideoFrame = app.DetectProcess(VideoFrame);
else
app.DetectingObjectsLamp.Enable = false;
app.TrackingObjectsLamp.Enable = true;
VideoFrame =app.TrackingProcess(VideoFrame);
end
%FlipSwitchがOnなら画像を上下反転させる
if strcmp(app.VerticalCntSwitch.Value,'On')
VideoFrame = flipud(VideoFrame);
end
% ビデオプレイヤーフレーム更新
app.Image.ImageSource = VideoFrame;
%End mytimer_func--------------------------------------------------
% 学習済み物体認識オブジェクトを生成
app.FaceDetector = vision.CascadeObjectDetector();
app.Eyes.Detector = vision.CascadeObjectDetector('EyePairSmall', 'UseROI', true);
app.Mouth.Detector = vision.CascadeObjectDetector('Mouth', 'UseROI', true);
%グレースケール画像を生成
GrayFrameImage = rgb2gray(VideoFrame);
%顔領域をグレースケール画像で特定
FaceBBox = app.FaceDetector.step(GrayFrameImage);
%、顔領域の中から目と口の位置を特定
app.Eyes.BBox = app.Eyes.Detector.step(GrayFrameImage,FaceBBox(1,:));
app.Mouth.BBox = app.Mouth.Detector.step(GrayFrameImage,FaceBBox(1,:));
%%------------------------------------------------------------------
%%物体位置特定に関係する関数
%%------------------------------------------------------------------
%------------------------------------------------------------------
%物体検出関数 フレームから 顔・目・口の位置を検出し、トラッカーに割り当てます
function [Return_VideoFrame] = DetectProcess(app,VideoFrame)
%グレースケール画像を生成
GrayFrameImage = rgb2gray(VideoFrame);
%顔領域をグレースケール画像で特定
FaceBBox = app.FaceDetector.step(GrayFrameImage);
%顔位置特定が成功したら
if ~isempty(FaceBBox)
%、顔領域の中から目の位置を特定
app.Eyes.BBox = app.Eyes.Detector.step(GrayFrameImage,FaceBBox(1,:));
%目の領域が特定できていたらVideoFrameに位置情報を重畳する。
if ~isempty(app.Eyes.BBox)
[VideoFrame,app.Eyes] = DetectObject(app,app.Eyes,VideoFrame);
end
%、顔領域の中から口の位置を特定
app.Mouth.BBox = app.Mouth.Detector.step(GrayFrameImage,FaceBBox(1,:));
%口の領域が特定できていたらVideoFrameに位置情報を重畳する。
if ~isempty(app.Mouth.BBox)
[VideoFrame,app.Mouth] = DetectObject(app,app.Mouth,VideoFrame);
end
end
Return_VideoFrame = VideoFrame;
end
%End DetectProcess-------------------------------------------------
function [ReturnVideoFrame,ReturnTarget] = DetectObject(app,Target,VideoFrame)
%グレースケール画像を生成
GrayFrameImage = rgb2gray(VideoFrame);
% 特定領域のコーナ点を特定する.
Points = detectMinEigenFeatures(GrayFrameImage, 'ROI', Target.BBox(1, :));
% ポイントトラッカーの初期化
Target = InitializeTrackObj(app,Points,GrayFrameImage,Target);
% Convert the box corners into the [x1 y1 x2 y2 x3 y3 x4 y4]
% ポリゴン描画のためのにポリゴン関数にあわせた座標に変換
bboxPolygon = reshape(Target.BBoxPoints', 1, []);
% 特定領域に長方形のポリゴンをフレームに重畳させる.
VideoFrame = insertShape(VideoFrame, 'Polygon', bboxPolygon, 'LineWidth', 3);
% 特定コーナをフレームに重畳させる.
VideoFrame = insertMarker(VideoFrame, Target.xyPoints, '+', 'Color', 'white');
ReturnVideoFrame = VideoFrame;
ReturnTarget = Target;
end
%%------------------------------------------------------------------
%%物体トラッキングに関係する関数
%%------------------------------------------------------------------
%------------------------------------------------------------------
%物体トラッキング関数 一つ前のフレームトラッキング結果から 
%トラッキング対象領域座標を更新する
function Return_VideoFrame = TrackingProcess(app,VideoFrame)
%目のトラッカーを更新
[VideoFrame,app.Eyes] = UpdateTrackObject(app,app.Eyes,VideoFrame);
%口のトラッカーを更新
[VideoFrame,app.Mouth] = UpdateTrackObject(app,app.Mouth,VideoFrame);
Return_VideoFrame = VideoFrame;
end
%End TrackingProcess--------------------------------------------------
%------------------------------------------------------------------
%オブジェクトトラッカーを更新する
function [ReturnVideoFrame,ReturnTarget] = UpdateTrackObject(app,Target,VideoFrame)
%グレースケール画像を生成
GrayFrameImage = rgb2gray(VideoFrame);
%グレーフレームから現在のトラックポイントを取得する
[xyPoints, isFound] = step(Target.Tracker, GrayFrameImage);
visiblePoints = xyPoints(isFound, :);
oldInliers = Target.OldPoints(isFound, :);
Target.NumPts = size(visiblePoints, 1);
%ターゲットのトラックポイントが10以上なトラックを継続する。
if Target.NumPts >= 10
% 一つ前のフレームで作成したポイント位置と
%現在のフレームのポイント位置のズレから2次元の幾何学変換を推定
[xform, inlierIdx] = estimateGeometricTransform2D(...
oldInliers, visiblePoints, 'similarity', 'MaxDistance', 4);
visiblePoints = visiblePoints(inlierIdx, :);
% 順方向アフィン変換でトラッキング領域を示すポリゴン座標を変換
Target.BBoxPoints = transformPointsForward(xform, Target.BBoxPoints);
% 特定した目領域を示す四角形の座標
% 座標形式を[x1 y1 x2 y2 x3 y3 x4 y4]に変換
bboxPolygon = reshape(Target.BBoxPoints', 1, []);
%平行四辺形にならないように調整
[Xmin,Xmax,Ymin,Ymax,bboxPolygon] = fixReqPoint(app,bboxPolygon);
% ポリゴンを画面に重畳.
%VideoFrame = insertShape(VideoFrame, 'Polygon', bboxPolygon, 'LineWidth', 3);
% トラックポイントを描画.
%videoFrame = insertMarker(videoFrame, visiblePoints, '+', 'Color', 'white');
% ポイント位置を更新
%EyeesPointの位置をメモリに記憶させる.
Target.OldPoints = visiblePoints;
setPoints(Target.Tracker,Target.OldPoints);
%錯視が有効ならばオブジェクトの位置のフレームを反転させる
if strcmp(app.IllusionCntSwitch.Value,'On')
VideoFrame(Ymin:Ymax,Xmin:Xmax,:) = flipud(VideoFrame(Ymin:Ymax,Xmin:Xmax,:));
end
end
ReturnVideoFrame = VideoFrame;
ReturnTarget = Target;
end
%------------------------------------------------------------------
%------------------------------------------------------------------
%トラッカーの初期化関数
function Target = InitializeTrackObj(app,Points,GrayFrameImage,Target)
% ポイントトラッカーの初期化
Target.xyPoints = Points.Location;
Target.NumPts = size(Target.xyPoints,1);
release(Target.Tracker);
initialize(Target.Tracker, Target.xyPoints, GrayFrameImage);
%1フレーム前のPointの位置を記憶させる.
Target.OldPoints = Target.xyPoints;
Target.BBoxPoints = bbox2points(Target.BBox(1, :));
end
%end InitializeTrackObjI-------------------------------------------
%------------------------------------------------------------------
%トラッカーのポリゴンフレームの座標を調整する。
function [Xmin,Xmax,Ymin,Ymax,bboxPolygon] = fixReqPoint(app,bboxPolygon)
bboxPolygon(1) = max(bboxPolygon(1),bboxPolygon(7));%X1
bboxPolygon(2) = max(bboxPolygon(2),bboxPolygon(4));%Y1
bboxPolygon(3) = max(bboxPolygon(3),bboxPolygon(5));%X2
bboxPolygon(4) = max(bboxPolygon(2),bboxPolygon(4));%Y2
bboxPolygon(5) = max(bboxPolygon(3),bboxPolygon(5));%X3
bboxPolygon(6) = max(bboxPolygon(6),bboxPolygon(8));%Y3
bboxPolygon(7) = max(bboxPolygon(1),bboxPolygon(7));%X4
bboxPolygon(8) = max(bboxPolygon(6),bboxPolygon(8));%Y4
Xmin = round(max(bboxPolygon(1),bboxPolygon(7))) - app.XExpand ;
Xmax = round(max(bboxPolygon(3),bboxPolygon(5))) + app.XExpand ;
Ymin = round(max(bboxPolygon(2),bboxPolygon(4))) - app.YExpand ;
Ymax = round(max(bboxPolygon(6),bboxPolygon(8))) + app.YExpand ;
end
%------------------------------------------------------------------
以下のコードは以前、Photoshopで切り抜いた画像から「両目、口の重心を求めその3点を結んだ三角形の重心」と「口の重心」を比較してどちらを向いているか検出するプログラムです。 
2つ目に教えていただきたい事は、これをリアルタイムで行う事が出来るのかという事と出来る場合、上のプログラムと下のプログラムをどのように組み合わせればよいかを教えていただきたいです。
%% Photoshop画像読込
Icolor = imread('/Users/tyt/Desktop/MATLAB/マネキン写真/正面中心.png');
I = rgb2gray(Icolor); % グレースケール化
bgc = I(10,10); % 背景色の選択
%% 目と口の重心を求める
BW = I > bgc + 1 | I < bgc - 1; % (ほぼ)背景と背景以外で2値化(imbinarize)
s = regionprops(BW,'centroid'); % イメージ内の連結要素の重心を計算
Areas = regionprops(BW,'Area'); % 各重心位置計算されたエリアの面積
centroids = cat(1,s.Centroid); % 重心を格納する構造体配列を単一の行列に連結
centroids = centroids(cat(1,Areas.Area) > 10, :); % 面積の大きな連結要素のみ選択
%% 目と口の重心点を結んだ三角形の重心を求める
triangle = polyshape(centroids(:,1),centroids(:,2)); % 重心点を結んだ三角形を定義
[trcntx,trcnty] = centroid(triangle); % 三角形の重心
sorted = sort(centroids(:,1)); % 目と口の重心点(x座標)をソート
%% グラフィック表示
imshow(Icolor);
hold on;
plot(triangle);
plot(trcntx,trcnty,'*','Color','k');
if sorted(2) - trcntx < 0 %(口の重心点)-(三角形の重心点)<0 なら右向きと表示
text(trcntx-40,trcnty+30,'右向き');
else
text(trcntx-40,trcnty+30,'左向き');
end
hold off;

採用された回答

Atsushi Ueno
Atsushi Ueno 2021 年 8 月 30 日
【1番目の質問】懸念が2点あります
  • 示されたコードはタイマオブジェクトの定義のみで、いつタイマが開始されたか判りません。app.Eyesを定義する前にapp.mytimer_func()が実行されてしまうと、app.Eyesが定義されていない旨のエラーが出る事になります。
  • 各コールバック関数はそれぞれ固有のスコープをもつので、この関数にアクセスする必要があるアプリの部分ごとに明示的にデータを共有しなければなりません。コードのコメント等から、mytimer_func()が別ファイルに格納されていると想定しますが、コールバック関数をappクラス内に書いてしまうのが一つの方法です。コールバック間のデータ共有 - MATLAB & Simulink - MathWorks 日本
  3 件のコメント
Atsushi Ueno
Atsushi Ueno 2021 年 8 月 31 日
動画は静止画像を繰り返し表示するものなので、静止画像で出来る事は動画でも出来ます。もちろん、処理速度が間に合うかどうかは気にしないとなりません。
タイマ割り込み関数内で認識オブジェクトの座標(Bounding Box)を含むフレームを取得しているので、下のプログラムをタイマ割り込み関数内から常時呼び出せば良いと思います。
ところで下のプログラムは、何もない背景に目と口が綺麗に切り出された画像を前提に、各オブジェクトの重心を求めてx座標における位置関係を判断するものでした。入力画像が「カメラ映像にBounding Boxが描かれた画像」だと下のプログラムは上手く機能しませんよ。目的(向きの検出)から考えると、正確な重心を求めなくても、Bounding Boxの中心座標でも十分な気がします。
tsuyoshi tsunoda
tsuyoshi tsunoda 2021 年 8 月 31 日
色々と教えていただきありがとうございます。
教えていただいた事を参考にやってみます。

サインインしてコメントする。

その他の回答 (0 件)

タグ

Community Treasure Hunt

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

Start Hunting!