AI時代的網站與手機App建置與開發Part24 - ML.NET與圖片異常偵測

 ·       摘要

將異常偵測應用在圖片資料能夠替代人工進行產品的瑕疵檢視, 例如檢視食物是否腐敗, 外型破裂, 研判X光片的內容是否異常, 或是判斷商品的包裝是否破損等等, 對於24小時不間斷的生產線能夠提供重要的品管支援.

: 圖片異常偵測示意圖

在本篇文章中, 我們將使用Microsoft ML.NET支援的ResNet50預訓練模型(Pre-Trained Model)執行圖片的異常偵測, 期能利用AI模型支援24小時不間斷的自動化作業.

·       準備訓練資料

首先請啟動瀏覽器, 瀏覽至Cloud and Non-Cloud Images(Anomaly Detection, 下載訓練圖片資料集(下載網址: https://www.kaggle.com/datasets/ashoksrinivas/cloud-anomaly-detection-images?resource=download).

1所示即為下載得到的圖片資料集的部分內容:

1: 訓練圖片資料集的部分內容

請將訓練圖片加入到Visual Studio專案中名稱為images資料夾中, 並到[屬性]視窗將所有的訓練圖片的[複製到輸出目錄]屬性的內容值設定為:有更新時才複製.

·       準備預訓練模型

請啟動瀏覽器, 瀏覽至ONNX Model Zoo, 下載支援圖片辨識的預訓練模型(下載網址: GitHub - onnx/models: A collection of pre-trained, state-of-the-art models in the ONNX format), 請下載名稱為resnet50-v2-7.onnx的預訓練模型.

請將下載妥的預訓練模型加入到Visual Studio專案中名稱為PreTrainedModel資料夾中, 並到[屬性]視窗將預訓練模型檔案的[複製到輸出目錄]屬性的內容值設定為:有更新時才複製.

·       使用ML.NET偵測圖片內容異常

首先我們要定義描述訓練圖片資料的ImageData類別和描述圖片特徵向量的ImageFeatures類別.

·       定義描述訓練圖片資料的ImageData類別

// 描述訓練圖片資訊的類別

public class ImageData

{

public string data { get; set; }        // 記載訓練圖片檔案名稱的屬性

}

·      定義描述圖片特徵向量的ImageFeatures類別

// 描述訓練圖片特徵向量的類別

public class ImageFeatures

{

// 指定輸出特徵向量的名稱(必須符合所使用的圖片辨識模型搭配)

    [ColumnName("resnetv24_dense0_fwd")]          

    public float[] Features { get; set; }   // 記載訓練圖片特徵向量的屬性

}

·       實作圖片異常偵測

//計算異常臨界值的函式

double CalculateAnomalyThreshold(List<float[]> features)

{

double totalDistance = 0;

    int count = features.Count;

    foreach (var feature in features)

    {

       totalDistance += CalculateDistanceToCenter(                           feature, features);      //加總距離中心點的距離

    }

    return totalDistance / count * 1.5;    //以平均距離的1.5倍為臨界值

}

//計算圖片的特徵向量距離中心點的距離

double CalculateDistanceToCenter(

float[] feature, List<float[]> features)

{

int length = feature.Length;

    var centroid = new float[length];

    foreach (var f in features)            // 加總圖片特徵向量的內容值

    {

      for (int i = 0; i < length; i++)

        entroid[i] += f[i];

    }

    for (int i = 0; i < length; i++)       // 計算圖片特徵向量的平均值

        centroid[i] /= features.Count;

 

    double distance=0; //計算並傳回圖片的Euclidean distance(歐幾里德距離)

    for (int i = 0; i < length; i++)

      distance += Math.Pow(feature[i] - centroid[i], 2);

    return Math.Sqrt(distance);

}

 

// 載入訓練圖片集

IEnumerable<ImageData> LoadImageData(string folder)

{

   foreach (var file in Directory.GetFiles(                                          folder, "*.jpg"))    // 讀取放置訓練圖片的資料夾中所有的JPG圖片檔案

   {

      yield return new ImageData {                                                  data = file };  // 將圖片的資料建立成ImageData類別的物件

   }

}

// 執行圖片異常偵測

private void btnTrain_Click(object sender, EventArgs e)

{

   string imagesFolder = "images";  // 指定放置訓練圖片的資料夾名稱

   string modelPath =                                                           "PreTrainedModel/resnet50-v2-7.onnx";  // 指定欲使用的預訓練模型

   var mlContext = new MLContext();         // 建立MLContext類別的物件

   var imageData = LoadImageData(imagesFolder).ToArray();//載入訓練圖片

   var data = mlContext.Data.LoadFromEnumerable(                               imageData);      // 準備成IDataView格式的訓練資料                                                                                                          var pipeline = mlContext.Transforms.LoadImages("data", "",

nameof(ImageData.data))  // 將圖片準備成預訓練模型指定的大小

          .Append(mlContext.Transforms.ResizeImages("data", 224, 224))

          .Append(mlContext.Transforms.ExtractPixels("data"))

          .Append(mlContext.Transforms.ApplyOnnxModel(

               modelFile: modelPath,

               outputColumnNames: new[] { "resnetv24_dense0_fwd" },

inputColumnNames: new[] { "data" }));

     var model = pipeline.Fit(data); // 使用指定的預訓練模型執行訓練

     var transformedData =

model.Transform(data);    // 計算每一張訓練圖片的特徵向量

     // 將訓練圖片的特徵向量準備成List集合

     var featuresList = new List<float[]>();

     foreach (var features in mlContext.Data.CreateEnumerable<

ImageFeatures>(transformedData, reuseRowObject: false))

     {

        featuresList.Add(features.Features);

     }

     var threshold = CalculateAnomalyThreshold(

featuresList);       //計算異常的臨界值

     int i = 0;

     foreach(var feature in featuresList)//標示特徵大於臨界值的圖片為異常

     {

       var distance = CalculateDistanceToCenter(

feature, featuresList);

       bool isAnomaly = distance > threshold;

       Trace.WriteLine($"Image: {imageData[i++].data},

Anomaly: {isAnomaly}, Distance: {distance}");

     }

}

執行上述的程式碼會顯示對每一個訓練圖片的內容預測的結果, 其中的Anomaly內容值為True代表圖片的內容被判定為異常, Anomaly內容值為False代表圖片的內容被判定為正常, 如圖2所示:

2: 判定圖片內容是否異常的結果

範例下載:

https://github.com/CraigIII/ImageAnomalyDetection.git

留言

這個網誌中的熱門文章

AI時代的網站與手機App建置與開發Part27 - ML.NET與物件偵測

AI時代的網站與手機App建置與開發Part28 - 使用YOLO模型進行物件偵測