個数のカウントで,累計数や位置座標の出力は可能でしょうか?

以下に個数のカウント方法の記載があるかと思います.

この方法の応用で,
検出したものの累計数のカウントは可能でしょうか?
また,通過した位置をCSVなどで出力することは可能でしょうか?
(上記リンクであれば,ビスケットの累計数とビスケットの位置情報)

可能な場合には追加のご質問です.
ランタイム評価のカメラモードで
例えば,カメラの前を通過した人数のカウントや,その人の位置,右から来たか?左から来たか?の識別は可能なのでしょうか?

画像ごとに評価しているという認識でしたので,
フェードインからフェードアウトするまでの同じ物体を,同じ物体として検出しているのか?フレームごとに違う物体として検出しているのか?が気になりました.

アドバイスいただけますと助かります.

「いいね!」 2

ご質問ありがとうございます。

検出したものの累計数のカウントと位置をCSVに出力についてですが、
スクリプト検査を用いれば可能です。

以下の動画を例に実装していきたいと思います。
tracking

まず、ペンを領域検出するAIを作成します。
作成したAIをランタイム評価画面で確認すると以下のような結果が得られます。
tracking2

次に累計カウント数と位置の出力をスクリプトで実装していきます。

using System.Text;
using System.IO;
// 検査をしない場合はnull 条件に一致する場合はtrue 条件に一致しない場合はfalse
bool? result = null;

// staticな変数を定義して画像間でカウントを共有
static int count = 0;

// ペンの位置を取得
var blobs = GetInspectedBlobs("ペン検出");

// ペンが映っている時
if (blobs?.Any() == true)
{
    // ファイルが存在しているか
    var hasFile = File.Exists(@"D:\work\debug\test.csv");
    // csvに書き込み 既にファイルがある場合は続きから出力
    using (var sw = new StreamWriter(@"D:\work\debug\test.csv",hasFile, Encoding.UTF8))
    {
        // ファイルが存在しなかった時にヘッダーを出力
        if (!hasFile)
        {
            // ヘッダー
            sw.WriteLine("重心X,重心Y,累計カウント");
        }
        var i = count;
        foreach (var blob in blobs)
        {
            i++;
            sw.Write(blob.Centroid.X);
            sw.Write(",");
            sw.Write(blob.Centroid.Y);
            sw.Write(",");
            sw.Write(i);
            sw.WriteLine("");
        }
    }
    count += blobs.Count;
    // ペンが映っている時はtrue
    result = true;
}
// ペンが映っていない時
else
{
    // ペンが映っていない時はfalse
    result = false;
}

// カウント数を表示するために左上にBlobを描画
var blob = CreateBlob(CreatePointD(50, 50), 100, 100);
AddBlob("累計カウント : " + count, blob);

// resultは必ず最後の行に記述してください
result

ポイントはstaticな変数を用いることで前の画像の時の情報を次の画像に渡せるというところです。

以下が結果になります。
累計カウントは下の緑の四角に表示されています。
tracking3

csvは以下のようになりました。

しかし、このままでは同じ物体かの区別がつかないので、累計カウントがどんどん増えてしまいます。
そのため、同じ物体か区別してあげる必要があります。

今回はシンプルに前の位置から距離が近いものは同一であるとみなす方法で実装します。

using System.Text;
using System.IO;
// 検査をしない場合はnull 条件に一致する場合はtrue 条件に一致しない場合はfalse
bool? result = null;

// staticな変数を定義して画像間でカウントを共有
static int count = 0;
// blobのidと位置を覚えておくディクショナリ
static Dictionary<int, IBlob> idBlobDictionary = new Dictionary<int, IBlob>();
// 距離閾値
const double distanceThreshold = 100.0;

// ペンの位置を取得
var blobs = GetInspectedBlobs("ペン検出");

// ペンが映っている時
if (blobs?.Any() == true)
{
    // ファイルが存在しているか
    var hasFile = File.Exists(@"D:\work\debug\test.csv");
    // csvに書き込み 既にファイルがある場合は続きから出力
    using (var sw = new StreamWriter(@"D:\work\debug\test.csv",hasFile, Encoding.UTF8))
    {
        // ファイルが存在しなかった時にヘッダーを出力
        if (!hasFile)
        {
            // ヘッダー
            sw.WriteLine("重心X,重心Y,前との距離,ID,累計カウント");
        }
        
        // トラッキング
        // 現在の画像でのペンの位置
        foreach (var blob in blobs)
        {
            var id = -1;
            var previousDistance = double.NaN;
            var blobPointD = CreatePointD(blob.Centroid.X, blob.Centroid.Y);
            // 前の画像のペンの位置
            foreach (var idBlob in idBlobDictionary)
            {
                var idBlobPointD = CreatePointD(idBlob.Value.Centroid.X, idBlob.Value.Centroid.Y);
                var distance = CalculateDistance(blobPointD, idBlobPointD);
                // 距離が近かったら同じものとみなす
                if (distance < distanceThreshold)
                {
                    // 前の画像と同じID
                    id = idBlob.Key;
                    // 情報を更新
                    idBlobDictionary[idBlob.Key] = blob;
                    previousDistance = distance;
                    break;
                }
            }

            // 前にいなかったペンだった時
            if (id == -1)
            {
                // 累計検出数を増やす
                count++;
                id = count;
                // ディクショナリに追加
                idBlobDictionary.Add(id, blob);
            }
            
            // ファイル書き込み
            sw.Write(blob.Centroid.X);
            sw.Write(",");
            sw.Write(blob.Centroid.Y);
            sw.Write(",");
            sw.Write(previousDistance);
            sw.Write(",");
            sw.Write(id);
            sw.Write(",");
            sw.Write(count);
            sw.WriteLine("");
        }
    }
    // ペンが映っている時はtrue
    result = true;
}
// ペンが映っていない時
else
{
    // 映らなくなったら前の情報を破棄
    idBlobDictionary.Clear(); 
    // ペンが映っていない時はfalse
    result = false;
}

// カウント数を表示するために左上にBlobを描画
var blob = CreateBlob(CreatePointD(50, 50), 100, 100);
AddBlob("累計カウント : " + count, blob);

// resultは必ず最後の行に記述してください
result

なお、staticな変数はスクリプト内で明示的に初期化処理をしない限りはランタイムを終了するまで初期化されないので注意してください。

そうすると以下のように同じペンは累計カウントに追加されなくなりました。
tracking4
csvには前の位置との距離とIDを追加してみました。

複雑なものをトラッキングしたい場合はトラッキングアルゴリズムを実装すれば、追跡が可能になると思います。
スクリプトの便利関数として実装も可能なので、今後そのあたりも充実させていきたいと思います。

また、時系列の異なる画像同士の情報を用いた検査は標準的な機能には実装していないため、
スクリプト検査での例を記述致しました。
トラッキング等の時系列を加味した検査に関しましては、今後検討していきたいと思います。

「いいね!」 3

早速の回答ありがとうございます.
試してみます.

「いいね!」 1

検出したものをカウントすることができました.

次は

staticな変数はスクリプト内で明示的に初期化処理をしない限りはランタイムを終了するまで初期化されない

の初期化の方法と,
同一画像でも2回検出してしまうときがあったため,精度の向上を試してみます.

ありがとうございました.

「いいね!」 1