ご質問ありがとうございます。
検出したものの累計数のカウントと位置をCSVに出力についてですが、
スクリプト検査を用いれば可能です。
以下の動画を例に実装していきたいと思います。
まず、ペンを領域検出するAIを作成します。
作成したAIをランタイム評価画面で確認すると以下のような結果が得られます。
次に累計カウント数と位置の出力をスクリプトで実装していきます。
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な変数を用いることで前の画像の時の情報を次の画像に渡せるというところです。
以下が結果になります。
累計カウントは下の緑の四角に表示されています。
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な変数はスクリプト内で明示的に初期化処理をしない限りはランタイムを終了するまで初期化されないので注意してください。
そうすると以下のように同じペンは累計カウントに追加されなくなりました。
csvには前の位置との距離とIDを追加してみました。
複雑なものをトラッキングしたい場合はトラッキングアルゴリズムを実装すれば、追跡が可能になると思います。
スクリプトの便利関数として実装も可能なので、今後そのあたりも充実させていきたいと思います。
また、時系列の異なる画像同士の情報を用いた検査は標準的な機能には実装していないため、
スクリプト検査での例を記述致しました。
トラッキング等の時系列を加味した検査に関しましては、今後検討していきたいと思います。