.net Framwork4.7.2 & VB.net でML.NETを試してみた(4)- トレニンーグモデルの再利用と実装

最近、Raspberry PiやLabveiwをやっていて、ML.NETのことを忘れていた。今日C#でRaspberryPi のTCP Service を作成するときにML.NETのことを思い出しました。

 

最近このようなを作成しました。

言語:VB.net  フレームワーク:.net Framwork4.7.2  ML.NET:1.51

その他:AForge 2.2.5 カメラ撮影ライブラリー

f:id:sesnail:20210111221140p:plain

カメラ認識

機能:

  1. 選択されたフォルダ内のトレニンーグデータでトレニンーグモデル作成します。
  2. カメラで作成した画像を認識します。
  3. 選択された画像を認識します。

 

Imports AForge.Video.DirectShow
Imports Microsoft.ML
Imports Microsoft.ML.Data
Imports Microsoft.ML.DataOperationsCatalog
Imports Microsoft.ML.Vision
Imports Microsoft.ML.Transforms
Imports System.IO
Imports Tensorflow.Data
Imports System.Diagnostics.Eventing.Reader
Imports System.Drawing.Imaging

Public Class FrmDataTrain
#Region "Machine Learning"
Class ImageData
Public ImagePath As String
Public Label As String
End Class
Class ModelOutput
Public ImagePath As String

Public Label As String
Public PredictedLabel As String
End Class
Class ModelInput
Public Image() As Byte
Public LabelAsKey As UInt32
Public ImagePath As String
Public Label As String
'Public score As Double
End Class

Dim projectDirectory As String = Path.GetFullPath(Path.Combine(AppContext.BaseDirectory, "../../../"))
Dim workspaceRelativePath As String = Path.Combine(projectDirectory, "workspace")
Public assetsRelativePath As String = Path.Combine(projectDirectory, "assets")
Dim theMLContext As MLContext = New MLContext()
Dim judgePicture As String
Dim testSet As IDataView
Dim images As IEnumerable(Of ImageData)
Dim theimageData As IDataView
Dim trainedModel As ITransformer
Dim predictionEngine As PredictionEngine(Of ModelInput, ModelOutput)
Dim modelSchema As DataViewSchema = Nothing
Public Iterator Function LoadImagesFromDirectory(ByVal folder As String, ByVal Optional useFolderNameAsLabel As Boolean = True) As IEnumerable(Of ImageData)
Dim files = Directory.GetFiles(folder, "*", SearchOption.AllDirectories)
For Each thefile In files

  1. If *1 Then

Continue For
Else
Dim theLabel = Path.GetFileName(thefile)
If useFolderNameAsLabel Then
theLabel = Directory.GetParent(thefile).Name
Else
For index = 0 To theLabel.Length
If (Not Char.IsLetter(theLabel(index))) Then
theLabel = theLabel.Substring(0, index)
Exit For
End If
Next
End If
Yield New ImageData With {.ImagePath = thefile, .Label = theLabel}
End If

Next

End Function


Private Sub TrainModel()
If TrainDataFolderBrowserDialog.SelectedPath <> Nothing Then
assetsRelativePath = TrainDataFolderBrowserDialog.SelectedPath
End If
images = LoadImagesFromDirectory(assetsRelativePath, True)
theimageData = theMLContext.Data.LoadFromEnumerable(images)
Dim shuffledData As IDataView = theMLContext.Data.ShuffleRows(theimageData)

Dim preprocessingPipeline As EstimatorChain(Of ImageLoadingTransformer) =
theMLContext.Transforms.Conversion.MapValueToKey("LabelAsKey", "Label").Append(
theMLContext.Transforms.LoadRawImageBytes("Image", assetsRelativePath, "ImagePath"))

Dim preProcessedData As IDataView = preprocessingPipeline.Fit(shuffledData).Transform(shuffledData)
Dim trainSplit As TrainTestData = theMLContext.Data.TrainTestSplit(preProcessedData, 0.3)
Dim validationTestSplit As TrainTestData = theMLContext.Data.TrainTestSplit(trainSplit.TestSet)
Dim trainSet As IDataView = trainSplit.TrainSet
Dim validationSet As IDataView = validationTestSplit.TrainSet
testSet = validationTestSplit.TestSet

Dim classifierOptions As ImageClassificationTrainer.Options = New ImageClassificationTrainer.Options With {
.FeatureColumnName = "Image",
.LabelColumnName = "LabelAsKey",
.ValidationSet = validationSet,
.Arch = ImageClassificationTrainer.Architecture.ResnetV2101,
.TestOnTrainSet = False,
.ReuseTrainSetBottleneckCachedValues = True,
.ReuseValidationSetBottleneckCachedValues = True,
.WorkspacePath = workspaceRelativePath
}
Dim trainingPipeline As EstimatorChain(Of KeyToValueMappingTransformer) = theMLContext.MulticlassClassification.Trainers.
ImageClassification(classifierOptions).Append(
theMLContext.Transforms.Conversion.MapKeyToValue("PredictedLabel"))
trainedModel = trainingPipeline.Fit(trainSet)
theMLContext.Model.Save(trainedModel, theimageData.Schema, "model.zip")

End Sub
Private Sub OutputPrediction(ByVal prediction As ModelOutput)
Dim imageName As String = Path.GetFileName(prediction.ImagePath)
Console.WriteLine($"Image: {imageName} | Actual Value: {prediction.Label} | Predicted Value: {prediction.PredictedLabel}")
Debug.WriteLine($"Image: {imageName} | Actual Value: {prediction.Label} | Predicted Value: {prediction.PredictedLabel}")

End Sub

'一枚画像を認識する
Public Function ClassifySingleImage(ByVal Image As ModelInput, Optional ByRef accqurace As Double = 0.2) As String
Dim prediction As ModelOutput = predictionEngine.Predict(Image)
OutputPrediction(prediction)

Return prediction.PredictedLabel
End Function
#End Region
Dim _videoSource As VideoCaptureDevice = Nothing
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles OpenCameraButton.Click
'ビデオ入力デバイスのみ取得
Dim videoDevices = New FilterInfoCollection(FilterCategory.VideoInputDevice)
If videoDevices.Count = 0 Then 'ビデオデバイスが無い
Return
End If
Dim MonikerString = videoDevices(0).MonikerString '最初のビデオデバイスを使用
_videoSource = New VideoCaptureDevice(MonikerString)
'AddHandler _videoSource.NewFrame, AddressOf Me.Video_NewFrame
VideoSourcePlayer1.VideoSource = _videoSource
_videoSource.Start()
End Sub

Private Sub Button2_Click(sender As Object, e As EventArgs) Handles StopCameraButton.Click
CloseCamera()
End Sub

'Private Sub Video_NewFrame(sender As Object, eventArgs As NewFrameEventArgs)
' VideoSourcePlayer1.VideoSource = _videoSource
' Dim img = DirectCast(eventArgs.Frame.Clone(), Bitmap)
'End Sub

'スクリーンショットを取る
Private Sub Button3_Click(sender As Object, e As EventArgs) Handles SnapshotButton.Click
Dim bitmappicture As Bitmap = VideoSourcePlayer1.GetCurrentVideoFrame()
Dim Name As String = System.DateTime.Now.ToString("yyyymmdd-HHMMss") & ".JPG"
bitmappicture.Save("pictures\\" & Name, ImageFormat.Jpeg)
ImageList1.Images.Add(bitmappicture)
With ListView1
.Items.Add(Name)
.Items(.Items.Count - 1).ImageIndex = ImageList1.Images.Count - 1
End With
End Sub

Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
VideoSourcePlayer1.AutoSizeControl = False
Try
trainedModel = theMLContext.Model.Load("model.zip", modelSchema)
predictionEngine = theMLContext.Model.CreatePredictionEngine(Of ModelInput, ModelOutput)(trainedModel)
Catch ex As Exception
Debug.Print(ex.ToString())
End Try
End Sub

'トレニンーグに使用する写真データのフォルダを参照しする
Private Sub SelectFolderButton_Click(sender As Object, e As EventArgs) Handles SelectFolderButton.Click
TrainDataFolderBrowserDialog.ShowDialog()
Try
If TrainDataFolderBrowserDialog.SelectedPath <> Nothing Then
assetsRelativePath = TrainDataFolderBrowserDialog.SelectedPath
ComboBox1.Items.Insert(0, assetsRelativePath)
ComboBox1.SelectedItem = ComboBox1.Items.Item(0)

End If
Catch
End Try
End Sub

Private Sub Form1_FormClosing(sender As Object, e As FormClosingEventArgs) Handles MyBase.FormClosing
CloseCamera()
End Sub

Sub CloseCamera()
If _videoSource Is Nothing Then
Return
End If

If _videoSource.IsRunning Then
_videoSource.SignalToStop() 'ビデオデバイスの停止
_videoSource.WaitForStop() '完全に停止するまで待つ
_videoSource = Nothing
End If
End Sub

'写真を選択する
Private Sub SelectFileButton_Click(sender As Object, e As EventArgs) Handles SelectFileButton.Click
If (PictureOpenFileDialog.ShowDialog() = DialogResult.OK) Then
judgePicture = PictureOpenFileDialog.FileName
End If
End Sub

'選択した写真を認識する
Private Sub JudgeButton_Click(sender As Object, e As EventArgs) Handles JudgeButton.Click
Dim thePicture As ImageData = New ImageData()
If ListView1.SelectedItems.Count <> 1 And judgePicture = Nothing Then
MessageBox.Show("写真一枚を選択してください")
Else
If ListView1.SelectedItems.Count = 1 Then
judgePicture = Application.StartupPath & "\pictures\" & ListView1.SelectedItems.Item(0).Text
With thePicture
.ImagePath = judgePicture
.Label = "Test"
End With

Else
With thePicture
.ImagePath = judgePicture
.Label = Path.GetFileName(Path.GetDirectoryName(judgePicture))
End With
End If
Dim theImages As IEnumerable(Of ImageData) = Enumerable.Empty(Of ImageData)
theImages = Enumerable.Append(theImages, thePicture)
'Dim Image As ModelInput = theMLContext.Data.CreateEnumerable(Of ModelInput)(testSet, True).First()
Dim testInputIDataView As IDataView = theMLContext.Data.LoadFromEnumerable(theImages)
Dim testInput As ModelInput

Dim PredictPreprocessingPipeline As EstimatorChain(Of ImageLoadingTransformer) =
theMLContext.Transforms.Conversion.MapValueToKey("LabelAsKey", "Label").Append(
theMLContext.Transforms.LoadRawImageBytes("Image", Nothing, "ImagePath"))

Dim unused As IDataView = PredictPreprocessingPipeline.Fit(testInputIDataView).Transform(testInputIDataView)
testInput = theMLContext.Data.CreateEnumerable(Of ModelInput)(unused, True).First()
Label2.Text = ClassifySingleImage(testInput)
End If
End Sub

'保存したモデルを削除し、再トレニンーグする
Private Sub TrainButton_Click(sender As Object, e As EventArgs) Handles TrainButton.Click
File.Delete("model.zip")
TrainModel()
End Sub

Private Sub ComboBox1_SelectedIndexChanged(sender As Object, e As EventArgs) Handles ComboBox1.SelectedIndexChanged
If ComboBox1.SelectedItem.ToString <> Nothing Then
judgePicture = ComboBox1.SelectedItem.ToString
End If
End Sub

Private Sub BtRetrain_Click(sender As Object, e As EventArgs) Handles BtRetrain.Click

End Sub
End Class

 

*1:Path.GetExtension(thefile) <> ".jpg") And (Path.GetExtension(thefile) <> ".png"