IC Imaging Control (.NET C#/Visual Basic)

  • サンプルプログラムトップページ
  • デバイスのオープンとハンドリング
    • C# VB.NET カメラの映像を既存のアプリケーションに組み込む方法
    • C# VB.NET デバイスを開く
    • C# VB.NET デバイスの開閉
    • C# VB.NET ダイアログボックスを使用してデバイスを開く
    • C# VB.NET 複数のデバイスを開く
    • C# VB.NET デバイスのシリアル番号を読み取る
    • C# VB.NET ICImagingControlの標準プロパティダイアログの使用
    • C# VB.NET デバイスプロパティ設定の保存と復元
  • ライブ表示
    • C# VB.NET ライブ表示のリサイズ
    • C# VB.NET フルスクリーン表示
    • C# VB.NET 十字マークのオーバーレイ
    • C# VB.NET WFPでvideoWindowを使ったビデオ表示
  • フレームフィルタ
    • C# VB.NET 低輝度、高輝度ピクセルの強調 その1
    • C# VB.NET 低輝度、高輝度ピクセルの強調 その2
  • ライブ表示とマルチプロセス
    • C# VB.NET ライブ表示のみ
    • C# VB.NET 静止画保存
    • C# VB.NET 任意のタイミングで画像処理と静止画保存を同時に処理
    • C# VB.NET 録画しながら任意のタイミングで静止画保存
    • C# VB.NET 全フレームリアルタイム画像処理しながら
             NG判定を静止画保存
    • C# VB.NET 全フレームリアルタイム画像処理しながら
             NG判定を静止画保存、同時に録画
  • イメージの取得
    • C# VB.NET イメージデータの取得
    • C# VB.NET 静止画ファイル保存(BMP)
    • C# VB.NET 静止画ファイル保存(JPEG)
    • C# VB.NET 静止画ファイルの連番保存
    • C# VB.NET カメラ画像保存&FTPで任意のサーバーに転送する
    • C# VB.NET リングバッファを使用したイメージシーケンスの取得と再生
    • C# VB.NET 静止画とAVI動画保存
    • C# VB.NET メモリーレコーディング
             (高速カメラで撮影した現象をRAMメモリー領域に保存)
    • C# VB.NET ソフトウェアトリガーで画像を表示・保存
    • C# VB.NET 録画時間を指定したAVIファイル保存
    • C# VB.NET 外部トリガーで録画の開始
    • C# VB.NET 取得したイメージの輝度値をCSV形式で出力する
             (16bit対応)
  • 画像処理
    • C# VB.NET イメージデータへのアクセス
    • C# VB.NET コントラストの強調 - Part 1
    • C# VB.NET コントラストの強調 - Part 2
    • C# VB.NET コントラストの強調 - Part 3
    • C# VB.NET レーザーポインタの位置を特定する
    • C# VB.NET OpenCVの画像処理
             (アナログタコメーターのデジタル化)
    • C# VB.NET AIモデルを使ったリアルタイム処理
  • Version 3.5サンプル
    • C# VB.NET 関心領域の画像更新
      Advanced Image Processing
    • C# VB.NET 2値化
      Binarization
    • C# VB.NET 明るさの調整
      BrightnessProperty
    • C# VB.NET 動画保存(MediaStreamSink)
      Capturing a Video File
    • C# VB.NET 動画保存(AviStartCapture)
      Capturing an AVI File
    • C# VB.NET 共通ファイル
      Common
    • C# VB.NET オーバーレイの作成
      Creating an Overlay
    • C# VB.NET デモアプリ
      demoapp
    • C# VB.NET VCDプロパティ:GPIOプロパティ編
      DigitalIO
    • C# VB.NET コールバック関数を使ってバッファを表示させる
      Display Buffer
    • C# VB.NET フィルターの適用
      Filter Inspector
    • C# VB.NET 静止画をキャプチャし保存する
      Grabbing an Image
    • C# VB.NET イメージバッファの輝度値を読み込み、輝度反転処理を施す
      Image Processing
    • C# VB.NET VCDプロパティ:全プロパティ一覧を表示する編
      List VCDProperties
    • C# VB.NET デバイス設定ダイアログの作成
      Making Device Settings
    • C# VB.NET イメージバッファにアクセスする
      Pixelformat
    • C# VB.NET コーデックのプロパティを保存する
      Saving Codec Properties
    • C# VB.NET スクロールとズーム
      croll And Zoom
    • C# VB.NET VCDプロパティ:露光とゲイン編
      StandardProperties
    • C# VB.NET VCDプロパティ:ストロボアウト編
      Strobe
    • C# VB.NET VCDプロパティ:
             プロパティダイアログ画面のカスタマイズ編
      VCD Property Page
    • C# VB.NET VCDプロパティ:明るさとホワイトバランス編
      VCD Simple Property
    • C# VB.NET VCDプロパティ:オートフォーカス編
    • C# VB.NET デバイスの再接続方法とフレーム落ちの確認
  • リンク集

    ICImagingControl WEB APIリファレンスマニュアル/サンプルプログラム

    :日本語対応済み :日本語化準備中
    開発言語 APIリファレンスマニュアル サンプルプログラム
    C 4.0
    ()
    - - 4.0
    ()
    - -
    C++ 4.0
    ()
    3.5
    ()
    3.4
    ()
    4.0
    ()
    3.5 3.4
    ()
    C#/VB.NET 4.0
    ()
    3.5
    ()
    3.4
    ()
    4.0
    ()
    3.5
    ()
    3.4
    ()
    Python 4.0
    ()
    3.5 3.4
    ()
    4.0
    ()
    3.5 3.4
    ()

AIモデルを使ったリアルタイム処理

概要

AISVisionを活用してAIモデルを作成し、それをThe Imaging Source社のSDKと統合することで、リアルタイムの画像処理を実現することが可能です。The Imaging Source社のSDKを使用してAI検査を行うシステムを構築する場合、以下のような概要と手順で進めることができます。

ASUS IoT AISVision
AISVisionとは

AISVisionは、ASUS IoT社が提供するノーコードのAIモデル作成ツールキットで、直感的なUIを通じて誰でも簡単にAIモデルを作成・検証できます。AIモデルを生成した後は、SDKを利用して既存のアプリケーションに容易に組み込むことができます。

サンプルプログラム

Software IC Imaging Control 3.5, Visual Studio™ 2022
サンプル(C#) ic35-aisvision_cs3.5.zip
使用しているライブラリ ・IC Imaging Control3.5
・AISVision1.6
・OpenCVSharp

サンプルツールの外観


ブロックの真ん中に亀裂があったらNGとしています。

実際に展示した時の動画です。

※動画を再生するにはvideoタグをサポートしたブラウザが必要です※

ステップ1:OK画像とNG画像の収集

1-1.画像の収集

IC Captureを使用して、ひび割れ検査用のOK画像(正常)とNG画像(異常)を静止画保存していきます。
(※画像形式: JPEG、BMP、PNG)

IC Capture2.5上で、スペースキーを押下することで静止画を簡単に保存できます。

「OK」フォルダと「NG」フォルダに作成し、それぞれ1000枚以上のパターンの画像を保存します。

1-2.カメラ設定の保存

露光時間やゲインなど、設定したカメラのパラメータをエクスポートして保存します。

デバイス設定のエクスポート XML形式で保存します。後述の【ステップ4:プログラム作成】のカメラ設定を呼び出す際に使用します。
構成ファイルの保存 IC Captureで同じパラメータを再利用する際に使用します。

ステップ2:AISVisionでAIモデルを作成

2-1.AISVisionのセットアップ

手順について下記のGPUドライバセットアップ、CUDAセットアップ、AISVisionセットアップまでセットアップしてください。
https://www.argocorp.com/software/DL/ASUS_IoT/index.html

なお、PCシステム要件は下記の通りです。

OS 64bit版 Windows 10, 11
CPU Intel® Core™ i7シリーズ(第12世代以降推奨)
メモリ 16GB以上
GPU NVIDIA® GeForce™ RTX3060以上、6GB以上必須

2-2.AISVisionのインストールと初期設定

USBドングルキーを挿入し、AISVisionを起動します。
リモートデスクトップ接続で起動しませんので注意してください。


2-3.AIモデルの作成

[トレーナー]を選択してくだい。

その後、プロジェクト画面で「ファイル名」と「タイプ」を選択後、[作成]ボタンをクリックしてください。ここではOK画像とNG画像をそれぞれ登録して区別するためにクラス分類を行っています。その他のモデルに関しては下記を参照してください。
https://www.argocorp.com/software/ASUS_IoT/AISVision.html

AIモデルの作成
Close
AIモデルの作成

次にカメラをカテゴリリストの「+」マークでOKとNGを作成し、OK画像とNG画像のそれぞれに対して【1-1.画像の収集】で保存した画像ファイルを入れます。

ブロック割れなしの画像(OK画像)の登録
AISVisionにOK画像を登録
Close
AISVisionにOK画像を登録

ブロック割れありの画像(NG画像)の登録
AISVisionにNG画像を登録
Close
AISVisionにNG画像を登録

2-4.画像の学習

次に[トレーナー]タブで選択した画像を学習します。
特にハイパーパラメータなどの設定は不要です。

[トレーナー]タブで選択した画像を学習
Close
[トレーナー]タブで選択した画像を学習
[トレーナー]タブで選択した画像を学習
Close
[トレーナー]タブで選択した画像を学習

2-5.AIモデルの出力

トレーニングが終了したら[ベリファイ]タブを選択し、モデルのエクスポートをクリックしAIモデル「ditoxファイル」を出力します。これでモデル生成は終了です。

AIモデルの出力
Close
AIモデルの出力

ステップ3:Visual Studioで使うための準備

3-1.AISVision Predict SDKのインストール

下記のURLに沿ってAISVision Predict SDKをダウンロードしてください。
https://www.argocorp.com/software/DL/ASUS_IoT/index.html#tab4


3-2.ライブラリなどの配置

SDKにある「bin」と「config」をソリューションファイルと同じフォルダ内に配置してください。また、「model」フォルダを作成し、ステップ2で作成した「ditoxファイル」を入れてください。


3-3.dllの参照設定

ソリューションエクスプローラ内の[参照]の上で右クリックをして、[参照の追加...]を選択し、「libxditk_aisvino_predictor_cswrapper」と「libxditk_aisv_predictor_cswrapper」を選択してください。


3-4.dllをexeと同じフォルダにコピー

exeファイルと同じ階層にAISVisionに関連するライブラリを配置するために、プロジェクトのプロパティにあるビルドイベント:ビルド後イベントのコマンドラインの箇所に下記のように記載してください。

set OUTPUT_DIRECTORY="$(TargetDir)model"
if not exist %OUTPUT_DIRECTORY% mkdir %OUTPUT_DIRECTORY%
copy "$(SolutionDir)bin" "$(TargetDir)"
copy "$(SolutionDir)model" "$(TargetDir)model"
copy "$(SolutionDir)config" "$(TargetDir)"

ビルド後に下記のように必要なファイル群がexeファイルと同じ階層に配置され、exeファイルが動作するようになります。

exeファイルの動作
Close
exeファイルの動作

ステップ4:プログラム作成

Form Loadのイベント

private void Form1_Load(object sender, EventArgs e)
{
   //経過時間計測用のカウンタ周期情報取得
   QueryPerformanceFrequency(ref timefrequencyCounter);

   //Log用ListBox準備
   var log_header = new ColumnHeader();
   log_header.Width = 700;
   log_header.Text = "Log";
   //LogBox.Columns.Add(log_header);

   //Result用ListBox準備
   var result_header = new ColumnHeader();
   result_header.Width = 700;
   result_header.Text = "Result";

   flag_closing = false;

   try
   {
       //AISVision初期化
       string dirpath = AppDomain.CurrentDomain.BaseDirectory + "model";
       string[] extensions = { ".ditox", ".ditir" };

       filepaths = Directory.EnumerateFiles(dirpath, "*.*", SearchOption.AllDirectories)
                   .Where(file => extensions.Contains(Path.GetExtension(file)))
                   .ToList();
       var filenames = filepaths.Select(Path.GetFileName).ToList();
       if (filenames.Count == 0)
       {
           MessageBox.Show("No AImodel On PC", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
           this.Close();
           return;
       }

       foreach (var name in filenames)
       {
           comboBox_modelpath.Items.Add(name);
       }

       if (comboBox_modelpath.Items.Count != 0)
       {
           comboBox_modelpath.SelectedIndex = 0;
           comboBox_modelpath.Enabled = true;
           button_modelload.Enabled = true;
       }

       //AIモデル読み込み
       readAIModel();

       //IC Imaging Control3.5でカメラオープン
       if (!icImagingControl1.DeviceValid)
       {
           icImagingControl1.LoadShowSaveDeviceState("lastDevice.xml");
       }
       else
       {
           MessageBox.Show("デバイスが見つかりませんでした。", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
           this.Close();
           return;
       }

       icImagingControl1.LiveDisplayDefault = false;
       icImagingControl1.LiveCaptureLastImage = false;
       icImagingControl1.LiveDisplay = false; //ライブ画像を表示しない
       icImagingControl1.Sink = new FrameQueueSink(mDataStream_NewBufferEvent, TIS.Imaging.MediaSubtypes.RGB32, 5);

       if(icImagingControl1.DeviceValid) icImagingControl1.LiveStart();
       icImagingControl1.LiveDisplayZoomFactor = (float)0.98f;



       this.WindowState = FormWindowState.Maximized;


       // PictureBox のサイズを更新
       pictureBox_result.Size = new System.Drawing.Size(
           (int)(icImagingControl1.Width * (float)1.5f),
           (int)(icImagingControl1.Height * (float)1.5f)
       );

   }
   catch (ICException ex)
   {
       addlog("ErrorType:" + ex.GetType().ToString());
       addlog("ErrorException:" + ex.GetBaseException().ToString());
       addlog("Error:Form1_Load");
   }
}

プログラムが起動すると、Form1_Load メソッドが呼び出され、以下の処理が実行されます。

  • (1)AISVisionの初期化

    AISVisionを初期化し、readAIModelメソッドを用いてAISVisionモデルを読み込みます。

  • (2)デバイス設定のロード

    icImagingControl1.LoadShowSaveDeviceState("lastDevice.xml") を使用して、以前に保存されたデバイス設定(lastDevice.xml:【1-2】で作成したxmlファイル)をロードし、カメラの設定ダイアログ画面を立ち上げます。

  • (3)リアルタイム処理の設定

    カメラをFrameQueueSinkに設定し、リアルタイム処理を可能にします。この設定により、カメラからのフレームデータが取得されるたびに、後述のmDataStream_NewBufferEventコールバック関数が呼び出されるようになります。

  • (4)ライブスタート

    icImagingControl1.LiveStart()を呼び出して、カメラの撮影を開始します。

AIモデルの読み込み

private void readAIModel() 
{

    //読み込み済みなら一度閉じる
    if (predict.CheckSession())
    {
        predict.Dispose();
        predict = new Predictor();
    }

    var path = filepaths.ElementAt(comboBox_modelpath.SelectedIndex);
    if (Path.GetFileName(path).Contains("ditox") == true)
    {
        //AIモデルファイル読み込み
        predict.LoadModel(path, true, 0);
        //読み込み成否チェック
        if (predict.CheckSession())
        {
            //モデル情報取得
            predict.GetModelInfo(out modelInfo);
            label_modelinfo.Text = modelInfo.ModelType.ToString();
            addlog("");
            addlog("AISVision Predicter Opened");
            addlog("Model Type: " + modelInfo.ModelType.ToString());
            addlog("Model Status: " + modelInfo.ModelStatus.ToString());
            addlog("Project Name: " + modelInfo.ProjectName.ToString());
            addlog("Task Name: " + modelInfo.TaskName.ToString());

            comboBox_classthreshold.Items.Clear();
            classlist.Clear();
            if (modelInfo.ModelType == Predictor.ModelType.Classification || modelInfo.ModelType == Predictor.ModelType.ObjectDetection || modelInfo.ModelType == Predictor.ModelType.OrientedObjectDetection)
            {
                numericUpDown_classthresholdvalue.Enabled = true;
                comboBox_classthreshold.Enabled = true;
                //カテゴリ情報取得
                Predictor.AisvCategoryInfo cat_temp = new Predictor.AisvCategoryInfo();
                predict.GetCategoryInfo(out cat_temp);
                for (int i = 0; i < cat_temp.Size; ++i)
                {
                    classlist.Add(cat_temp.CategoryList[i].Name, cat_temp.CategoryList[i].Threshold);
                    categorylist.Add(i, cat_temp.CategoryList[i].Name);
                    categoryfontlist.Add(i, cat_temp.CategoryList[i].Color);
                    comboBox_classthreshold.Items.Add(cat_temp.CategoryList[i].Name);
                }
                comboBox_classthreshold.SelectedIndex = 0;
                numericUpDown_classthresholdvalue.Value = (decimal)classlist[cat_temp.CategoryList[0].Name];
                target = comboBox_classthreshold.Text;
            }
            else
            {
                numericUpDown_classthresholdvalue.Enabled = false;
                comboBox_classthreshold.Enabled = false;
            }
        }
        else
        {
            predict.Dispose();
            addlog("Error: Faild AISVision Predicter Loading");
        }
    }
    else
    {
        addlog("Please select *.ditox file");
    }

}

モデルフォルダ内に存在する.ditoxファイルを確認し、predict.LoadModel(path, true, 0)を使用してモデルをロードします。その後、CheckSessionを再度呼び出して、モデルが正しくロードされたかを確認します。さらに、GetModelInfoを利用してモデルの詳細情報(モデルタイプ、状態、プロジェクト名、タスク名など)を取得し、ログやUIに反映します。
ロードされたモデルのタイプが「分類」「物体検出」「方向付き物体検出」のいずれかである場合、しきい値やカテゴリ情報を取得し、スライダーやドロップダウンを有効化します。それ以外の場合、これらのUI要素は無効化されます。
ロードが失敗した場合、エラーログを出力し、predict.Dispose() を呼び出してリソースを解放します。
※Windowsのリモートデスクトップ接続でアプリを起動したときにはモデルの読み込みが行えませんのでご注意ください。


コールバック関数

private FrameQueuedResult mDataStream_NewBufferEvent(IFrameQueueBuffer buffer)
{

    // フレームが閉じている場合は再キューイングを行う
    if (flag_closing) return FrameQueuedResult.ReQueue;

    if (buffer == null)
    {
        // データが格納されていない場合のエラーログ
        addlog("Error: Buffer Timeout ");
    }
    else
    {
        // 非同期タスクで画像処理を実行
        Task.Run(() =>
        {
            IFrameQueueBuffer controlbuffer = buffer;
            icImagingControl1.DisplayImageBuffer(buffer);
            resultsimage = Enumerable.Repeat<Bitmap>(new Bitmap(cam_width, cam_height, PixelFormat.Format8bppIndexed), num_resultview).ToArray();

            // バッファからBitmapを作成し、Matに変換
            Mat matImage = OpenCvSharp.Extensions.BitmapConverter.ToMat(buffer.CreateBitmapWrap());
            if (matImage != null)
            {
                // 青チャンネル(Bチャンネル)のみを抽出
                Mat blueChannel = new Mat();
                Cv2.ExtractChannel(matImage, blueChannel, 0); // Bチャンネルはインデックス0

                // 全体の明るさを補正する(ガンマ補正で暗い青を明るくする)
                Mat adjustedBlueChannel = new Mat();
                blueChannel.ConvertTo(adjustedBlueChannel, MatType.CV_8U, alpha: 1.5, beta: 20);
                // alpha: コントラスト調整係数、beta: 明るさ補正値

                // 画像の中心座標を計算
                int centerX = adjustedBlueChannel.Cols / 2; // 画像幅の中心
                int centerY = adjustedBlueChannel.Rows / 2; // 画像高さの中心

                // 真ん中の5x5領域を抽出
                int rectSize = 5; // 矩形サイズ
                int startX = Math.Max(0, centerX - rectSize / 2); // X方向の開始座標
                int startY = Math.Max(0, centerY - rectSize / 2); // Y方向の開始座標
                int width = Math.Min(rectSize, adjustedBlueChannel.Cols - startX); // 幅
                int height = Math.Min(rectSize, adjustedBlueChannel.Rows - startY); // 高さ

                // 抽出領域(Region of Interest)を設定
                Rect roi = new Rect(startX, startY, width, height);
                Mat roiMat = new Mat(adjustedBlueChannel, roi); // 抽出領域を切り出す

                // 5x5領域の輝度平均を計算
                Scalar meanBrightness = Cv2.Mean(roiMat);

                // 輝度平均をログに出力
                addlog("5x5領域の輝度平均: " + meanBrightness.Val0.ToString());
                double meanValue = meanBrightness.Val0;

                // 輝度平均が75を超える場合、バッファをコピー
                if (meanValue > 75)
                {
      //AI画像処理処理へ
                    copybuf(controlbuffer);
                }
            }
        });
    }
    return FrameQueuedResult.ReQueue;

}

mDataStream_NewBufferEventは、カメラからフレームデータが送られてくるたびに発火するコールバックイベントです。このイベントでは、全フレームを受け取った後に、処理がフリーズしないよう非同期タスク (Task.Run)を使用してフレームごとに画像処理を行います。
各フレームに対して以下の処理が実行されます:

  • (1)バッファのチェック
    • バッファが空 (null) の場合はエラーログを出力。
    • バッファが有効であれば、次の処理へ進みます。
  • (2)画像処理
    • フレームデータをBitmapに変換し、さらに OpenCV のMat形式に変換します。
    • 画像の青チャンネル(Bチャンネル)を抽出し、ガンマ補正を行って全体の明るさを調整します。
    • 画像の中心座標を基準に5×5ピクセルの領域(ROI: Region of Interest)を抽出し、その輝度の平均値を計算します(下図赤い資格の所のピクセル平均値)。
  • (3)3. 輝度値の条件チェック
    • 平均輝度値が 75 を超える場合、copybufメソッドを呼び出してAI画像処理を実行します。

リアルタイムでフレームデータを処理しつつ、非同期処理を利用することでメインスレッドのフリーズを防ぎ、スムーズな操作を可能にしています。

表示用Bitmapへのコピーと表示処理

private void copybuf(IFrameQueueBuffer buffer)
{

   if (flag_closing) return;
   if (imagelock) return;
   if (rwLock.TryEnterWriteLock(1))
   {
       try
       {

           currentimage = buffer.CreateBitmapWrap();
           imagelock = true;

            //AISVision推論用バッファ作成
            aisimage_source = predict.ConvertBitmapToImage(buffer.CreateBitmapWrap());

           //AISVision推論
           predictbuf();
       }
       finally
       {
           if (rwLock.IsWriteLockHeld)
           {
               rwLock.ExitWriteLock();
           }
       }

       //メインスレッドでUI更新
       this.BeginInvoke(new Action(() =>
       {
               pictureBox_result.Invalidate();
               flag_paintmain = false;
       }));

   }

}

copybufメソッドは、IFrameQueueBufferのデータをコピーし、AI推論を実行するための準備を行います。このメソッドでは、以下のような排他制御やロック処理を実施しています。

  • (1)flag_closing
    • アプリケーションが終了中であれば処理をスキップします。
  • (2)imagelock
    • 他の処理が画像データを使用中の場合、処理をスキップして競合を回避します。
  • (3)rwLock
    • 書き込みロックを取得し、排他制御を行います。これにより、同時アクセスによるデータ競合を防ぎます。

処理の流れは以下の通りです。

  • (1)フレームデータのコピー
    • バッファから Bitmap を生成し、currentimage にコピーします。
    • 同時に、AI推論用に aisimage_source を作成します。
  • (2)AI推論の実行
    • predictbuf メソッドを呼び出し、aisimage_source を使用してAI推論を実行します。
  • (3)ロックの解放
    • 処理が完了したら、rwLock のロックを解放します。
  • (4)UIの更新
    • BeginInvoke を用いて非同期でUIスレッドを更新し、pictureBox_result の再描画します。

リアルタイムなAI推論と画像表示をスムーズに行い、アプリケーション全体の安定性とパフォーマンスを確保しています。


AI推論

private void predictbuf()
{
    // フォームが閉じられている場合は処理を終了
    if (flag_closing) return;

    // 書き込みロックを取得
    if (predictLock.TryEnterWriteLock(1))
    {
        ulong checkstart = 0;
        ulong checkend = 0;

        try
        {
            // 処理時間計測:開始ポイント
            QueryPerformanceCounter(ref checkstart);

            // 推論実行
            predictInfo = predict.Predict(aisimage_source);

            // 処理時間計測:終了ポイント
            QueryPerformanceCounter(ref checkend);

            // 結果画像生成
            aisimage_result = predict.VisualizeResult(aisimage_source, predictInfo, true);
            predictimage = predict.ConvertImageToBitmap(aisimage_result);
        }
        catch (OperationCanceledException)
        {
            // タスクがキャンセルされた場合の処理
            return;
        }
        finally
        {
            // 書き込みロックを解放
            if (predictLock.IsWriteLockHeld)
            {
                predictLock.ExitWriteLock();
            }
        }

        // 処理時間計算
        var processingtime = ((checkend - checkstart) * 1000 / timefrequencyCounter);

        // メインスレッドでUI更新
        this.BeginInvoke(new Action(() =>
        {
            label_predicttime.Text = "Predict processing time: " + (processingtime).ToString() + " ms";
        }));

        // 推論結果表示
        if (resultLock.TryEnterWriteLock(1))
        {
            try
            {
                // モデルタイプに応じた結果処理
                //モデルタイプ:アノマリー
                if (modelInfo.ModelType == Predictor.ModelType.AnomalyDetection)
                {
                    addresult("[Anomaly] Result:" + predictInfo.anomalydetectionInfo.Name + " [" + Math.Round(predictInfo.anomalydetectionInfo.Score, 2, MidpointRounding.AwayFromZero).ToString() + "%]");
                    if (predictInfo.anomalydetectionInfo.Name == target)
                    {
                        if (!flag_detect)
                        {
                            for (int i = 0; i < resultsimage.Length; ++i)
                            {
                                if (i != num_resultview - 1)
                                {
                                    resultsimage[i] = resultsimage[i + 1];
                                }
                                else
                                {
                                    resultsimage[i] = predictimage;
                                }
                            }
                            flag_detect = true;
                        }
                    }
                    else
                    {
                        flag_detect = false;
                    }
                }
                //モデルタイプ:クラス分類
                else if (modelInfo.ModelType == Predictor.ModelType.Classification)
                {
                    addresult("[Classification] Result:" + predictInfo.classificationInfo.Name + " [" + Math.Round(predictInfo.classificationInfo.Prob, 2, MidpointRounding.AwayFromZero).ToString() + "%]");

                    this.BeginInvoke(new Action(() =>
                    {
                        if (predictimage != null)
                        {
                            pictureBox_result.Image = (System.Drawing.Image)predictimage.Clone();
                        }
                    }));

                    string_Judgement = predictInfo.classificationInfo.Name + " [" + Math.Round(predictInfo.classificationInfo.Prob, 2, MidpointRounding.AwayFromZero).ToString() + "%]";

                    if (check_saveimage.Checked && imagelock) SaveImageBasedOnJudgement((Bitmap)currentimage.Clone(), predictInfo.classificationInfo.Name, Math.Round(predictInfo.classificationInfo.Prob, 2, MidpointRounding.AwayFromZero).ToString());
                    imagelock = false;

                    if (predictInfo.classificationInfo.Name == target)
                    {
                        if (!flag_detect)
                        {
                            flag_detect = true;
                        }
                    }
                    else
                    {
                        flag_detect = false;
                    }
                }
                //モデルタイプ:物体検出
                else if (modelInfo.ModelType == Predictor.ModelType.ObjectDetection)
                {
                    addresult("[ObjectDetection] Detected items:" + predictInfo.objectdetectionInfo.Size.ToString());
                    foreach (var items in predictInfo.objectdetectionInfo.ResultList)
                    {
                        addresult("[" + items.Name + "] [Prob=" + Math.Round(items.Prob, 2, MidpointRounding.AwayFromZero).ToString() + "] [X=" + Math.Round(items.TopLeftX) + ",Y=" + Math.Round(items.TopLeftY) + "] [Width=" + Math.Round(items.Width) + ",Height=" + Math.Round(items.Height) + "]");
                    }
                    if (predictInfo.objectdetectionInfo.ResultList.Any(res => res.Name == target))
                    {
                        if (!flag_detect)
                        {
                            for (int i = 0; i < resultsimage.Length; ++i)
                            {
                                if (i != num_resultview - 1)
                                {
                                    resultsimage[i] = resultsimage[i + 1];
                                }
                                else
                                {
                                    resultsimage[i] = predictimage;
                                }
                            }
                            flag_detect = true;
                        }
                    }
                    else
                    {
                        flag_detect = false;
                    }
                }
                //物体検出(回転あり)
                else if (modelInfo.ModelType == Predictor.ModelType.OrientedObjectDetection)
                {
                    addresult("[OrientedObjectDetection] Detected items:" + predictInfo.orientedobjectdetectionInfo.Size.ToString());
                    foreach (var items in predictInfo.orientedobjectdetectionInfo.ResultList)
                    {
                        addresult("[" + items.Name + "] [Prob=" + Math.Round(items.Prob, 2, MidpointRounding.AwayFromZero).ToString() + "] [CenterX=" + Math.Round(items.CenterX) + ",CenterY=" + Math.Round(items.CenterY) + "] [Width=" + Math.Round(items.Width) + ",Height=" + Math.Round(items.Height) + "] [Angle=" + items.Angle + "]");
                    }
                    if (predictInfo.orientedobjectdetectionInfo.ResultList.Any(res => res.Name == target))
                    {
                        if (!flag_detect)
                        {
                            for (int i = 0; i < resultsimage.Length; ++i)
                            {
                                if (i != num_resultview - 1)
                                {
                                    resultsimage[i] = resultsimage[i + 1];
                                }
                                else
                                {
                                    resultsimage[i] = predictimage;
                                }
                            }
                            flag_detect = true;
                        }
                    }
                    else
                    {
                        flag_detect = false;
                    }
                }
                //モデルタイプ:セグメンテーション
                else if (modelInfo.ModelType == Predictor.ModelType.Segmentation)
                {
                    addresult("[Segmentation] Detected items:" + predictInfo.segmentationInfo.CategorySize.ToString());
                    foreach (var items in predictInfo.segmentationInfo.CategoryList)
                    {
                        addresult("[" + items.Name + "] [Threshold=" + items.Threshold + "]");
                    }
                    if (predictInfo.segmentationInfo.CategoryList.Any(res => res.Name == target))
                    {
                        if (!flag_detect)
                        {
                            for (int i = 0; i < resultsimage.Length; ++i)
                            {
                                if (i != num_resultview - 1)
                                {
                                    resultsimage[i] = resultsimage[i + 1];
                                }
                                else
                                {
                                    resultsimage[i] = predictimage;
                                }
                            }
                            flag_detect = true;
                        }
                    }
                    else
                    {
                        flag_detect = false;
                    }
                }
            }
            finally
            {
                // 書き込みロックを解放
                if (resultLock.IsWriteLockHeld)
                {
                    resultLock.ExitWriteLock();
                }
            }
        }
    }
}

このコードでは、AIモデルによる推論処理を行い、その結果を処理・表示する流れが実装されています。

推論処理

カメラから送られてきたフレーム(aisimage_source)を基に以下の推論処理が実行されます:

  • (1)Predict
    • 推論結果は predictInfo オブジェクトに格納されます。
  • (2)VisualizeResult
    • 推論結果を基に、結果を可視化した画像を生成します。
  • (3)ConvertImageToBitmap
    • 可視化された結果画像を Bitmapに変換し、UIで表示可能な形式にします。
推論結果の処理

AIモデルの種類に応じて適切な処理が行われます:

  • (1)異常検出(Anomaly Detection)
    • 異常が検出された場合、該当する領域をログに記録し、フラグを更新します。
    • 判定が目標(target)と一致した場合、結果画像を更新します。
  • (2)クラス分類(Classification)
    • 推論結果をUIに表示します。
    • 推論結果のクラス名と確率を string_Judgement に格納します。
    • チェックボックスにマークが付いている場合、判定結果に応じてSaveImageBasedOnJudgementメソッドで画像を保存します。
  • (3)物体検出(Object Detection)
    • 検出されたオブジェクトの名前、確率、位置、サイズをログに記録します。
    • 目標クラス(target)が検出された場合、結果画像を更新します。
  • (4)物体検出(回転あり Oriented Object Detection)
    • 回転情報を含む検出結果を処理します。
    • 検出対象の中心座標やサイズ、角度をログに記録します。
  • (5)セグメンテーション(Segmentation)
    • 検出されたカテゴリ情報をログに記録します。
    • 指定したカテゴリが存在する場合、結果画像を更新します。

画像保存処理

private void SaveImageBasedOnJudgement(Bitmap saveImage, string judgement, string probability)
{
    // 保存先のベースディレクトリを指定
    string baseDirectory = @"D:\ImageResults"; // 必要に応じて変更
    string subDirectory;

    // Judgementに応じてフォルダを決定
    switch (judgement.ToLower())
    {
        case "ok":
            subDirectory = "OK";
            break;
        case "ng":
            subDirectory = "NG";
            break;

        default:
            subDirectory = "unknown";
            break;
    }

    // フォルダパスを組み立てる
    string targetDirectory = Path.Combine(baseDirectory, subDirectory);

    // フォルダが存在しない場合は作成
    if (!Directory.Exists(targetDirectory))
    {
        Directory.CreateDirectory(targetDirectory);
    }
    counter_save++;
    // ファイル名をユニークにするためにタイムスタンプを使用
    string fileName = $"Image_{subDirectory}_{probability}_{counter_save}_{DateTime.Now:yyyyMMdd_HHmmssfff}.bmp";
    string filePath = Path.Combine(targetDirectory, fileName);

    // Bitmap を指定したパスに保存
    saveImage.Save(filePath, System.Drawing.Imaging.ImageFormat.Bmp);

    addlog($"Image saved to: {filePath}");
}

クラス分類の場合、チェックボックスが有効な際には画像を保存することが可能です。
この処理はSaveImageBasedOnJudgementメソッドで行われます。

SaveImageBasedOnJudgementの主な処理
  • (1)保存先のベースディレクトリ
    • 画像はD:\ImageResults配下に保存されます。必要に応じて変更してください。
  • (2)判定結果に応じたサブフォルダの選択
    • OK:判定が「OK」の場合
    • NG:判定が「NG」の場合
    • unknown:判定が不明な場合
  • (3)フォルダの生成
    • サブフォルダが存在しない場合、新しく作成します。
  • (4)ユニークなファイル名の生成
    • タイムスタンプと保存カウンタ(counter_save)を用いてファイル名を生成し、重複を回避します。
  • (5)画像の保存
    • 生成したファイルパスに画像を Bitmap 形式で保存します。
    • 保存後、保存先のパスをログに記録します。
▲ このページの先頭に戻る

Copyright © ARGO Corporation. All Rights Reserved.