MultiTread application on Raspberry Pi with C# .NET 5.0

Continue with the Raspberry Pi work.

 

Deliverables

Temperature Reading

Read the temperature value form DS18B20 int a sub thread, this is because the temperature reading really cost some time.

Everytime it read the temperature value, send the value to the client with a TCP connection which the port Number is 1300(Mainely for sending messages). 

PWM Adjustment

 As I wish to make a PID controller, so ,To accomplish this goal, also need to adjust the PWM timely. I'm prepared to use Labview to control PWM with anothe port: 1200(Mainely for recieving messages). 

As the two reason above,  it's better to use multiple thread to implement the program.

 

ScreenShot

 f:id:sesnail:20210208194840p:plain

 

code

using System;
using System.Net;
using System.Net.Sockets;
using System.Device.Gpio;
using System.Device.Pwm.Drivers;
using System.Threading;
using System.Threading.Tasks;
using Iot.Device.OneWire;
using System.Text;
using System.Collections.Generic;

namespace ConsoleApp1
{
    class Program
    {

         static  void Main(string[] args)
            {
                OneWireThermometerDevice theDs = null;
                GpioController controller = new GpioController();

                //TCP connection define
                int PortTemperature = 1300;
                int PortPWM = 1200;
                IPAddress serverAddr = IPAddress.Parse("192.168.11.10");
                TcpListener listenerTemp = null;
                TcpListener listenerPWM = null;
                NetworkStream streamTemp = null;
                NetworkStream streamPWM = null;
                string temperature = string.Empty;
                float pwm = 0;

                //PWM define
                int pin = 18;
                SoftwarePwmChannel theChannel = null;
                DeviceInitialize(ref theChannel, ref controller, pin, ref theDs);
                Console.WriteLine("Blinking LED. Press Ctrl+C to end.");
            #region TCP
            // Set the TcpListener on port 13000.
            Task.Run(() => ThreadReadTemperature());
            // Enter the listening loop.
            while (true)
                {
                    //pwm = float.Parse(Console.ReadLine());
                    //Console.Write(pwm);
                    LEDSwitch();
                }

                //// Shutdown and end connection
                //client.Close();
                #endregion
                void ThreadReadTemperature()
                {
                    try
                    {

                        while (true)
                        {
                            Byte[] sendBytes = null;

                            Console.Write("Temperature Waiting for a connection... ");
                            // TcpListener server = new TcpListener(port);
                            listenerTemp = new TcpListener(serverAddr, PortTemperature);
                            listenerTemp.Start();
                            // Perform a blocking call to accept requests.
                            TcpClient client = listenerTemp.AcceptTcpClient();
                            Console.WriteLine("Temperature Connected!");

                            // Get a stream object for reading and writing
                            streamTemp = client.GetStream();
                            // Loop to receive all the data sent by the client.

                            // Start listening for client requests.
                           
                            while (true)
                            {
                                temperature = theDs.ReadTemperature().ToString();
                                sendBytes = Encoding.ASCII.GetBytes(temperature);
                                streamTemp.Write(sendBytes, 0, sendBytes.Length);
                                Console.WriteLine(temperature);
                            }
                        }
                    }
                    catch (SocketException e)
                    {
                        Console.WriteLine("SocketException: {0}", e);
                        // Stop listening for new clients.
                        listenerTemp.Stop();
                    }
                }
                void LEDSwitch()
                {

                    try
                    {
                        Byte[] readBytes = new Byte[256];

                        Console.Write("PWM Waiting for a connection... ");
                        // TcpListener server = new TcpListener(port);
                        listenerPWM = new TcpListener(serverAddr, PortPWM);

                        // Start listening for client requests.
                        listenerPWM.Start();
                        // Perform a blocking call to accept requests.
                        TcpClient client = listenerPWM.AcceptTcpClient();
                        Console.WriteLine("PWM Connected!");

                        // Get a stream object for reading and writing
                        streamPWM = client.GetStream();
                        // Loop to receive all the data sent by the client.
                        int i;

         
                            while ((i = streamPWM.Read(readBytes, 0, readBytes.Length)) != 0)
                            {
                                pwm = float.Parse(Encoding.ASCII.GetString(readBytes));
                                Console.WriteLine($"PWM:  {pwm}");
                                Console.WriteLine(temperature);
                                if(0<=pwm && pwm<=1)theChannel.DutyCycle = pwm;
                            
                            //streamPWM.Flush();
                        }
                    
                    }
                    catch (SocketException e)
                    {
                        Console.WriteLine("SocketException: {0}", e);
                        // Stop listening for new clients.
                        listenerPWM.Stop();
                        controller.ClosePin(pin);
                        theChannel.Stop();
                    }

                    //controller.Write(pin, ((ledOn) ? PinValue.High : PinValue.Low));
                    //ledOn = !ledOn;
           }
                void DeviceInitialize(ref SoftwarePwmChannel channel, ref GpioController theController, int ControlPin, ref OneWireThermometerDevice theDal)
                {
                    string busID = "", devieID = "";
                    channel = new SoftwarePwmChannel(ControlPin, 400, 0.5, false, theController, false);
                    channel.Start();
                    //theController.OpenPin(ControlPin, PinMode.Output);
                    foreach (var device in OneWireThermometerDevice.EnumerateDevices())
                    {
                        busID = device.BusId;
                        devieID = device.DeviceId;
                        Console.WriteLine(busID + "," + devieID + ","+device.Family);
                    }
                    theDal = new OneWireThermometerDevice(busID, devieID);
                
                }
            }
    }
}

Read DS18B20 temperature with .NET 5.0 IoT throught TCP on Raspberry Pi 3B+

These days I was thinking about to  make a PID controller use my Raspberry Pi Which has been layed in the trash corner for a long time.

At firsh I planned to do it with Labview only,but I found it is kind of difficult to make it come true.Thought there is a strong LINX tool with which you can operate the GPIO of  Raspberry Pi directly, it didn't offer the One-Wire sensor like DS18B20. And with unknow reason the LINX don't have the custom command for Raspberry Pi, so I have to abandent.

Fortunately, I found a good another way  to do it.

 

That is .NET Window IoT.

 Microsoft also creates some tutorials. You can use .net 5.0 operate the GPIO directly.

And  the .NET IoT Libraries 1.3.0 has lots of libraries that is very convenient to use.

docs.microsoft.com

 

 

R1: 4.7K Ohm or 10K Ohm resistor

f:id:sesnail:20210206134325p:plain



 

CODE:

using System;
using System.Net;
using System.Net.Sockets;
using System.Device.Gpio;
using System.Threading;
using Iot.Device.Pwm;
using Iot.Device.OneWire;
using System.Text;

namespace ConsoleApp1
{
class Program
{

static void Main(string args)
{
OneWireThermometerDevice theDs = null;
GpioController controller = new GpioController();
int pin=18;
bool ledOn = true;
Console.WriteLine("Blinking LED. Press Ctrl+C to end.");
TcpListener server = null;
DeviceInitialize(ref controller, pin, ref theDs);
#region TCP
try
{
// Set the TcpListener on port 13000.
int port = 1300;
IPAddress serverAddr = IPAddress.Parse("192.168.11.10");

// TcpListener server = new TcpListener(port);
server = new TcpListener(serverAddr, port);

// Start listening for client requests.
server.Start();

// Enter the listening loop.
while (true)
{
Console.Write("Waiting for a connection... ");
// Perform a blocking call to accept requests.
TcpClient client = server.AcceptTcpClient();
Console.WriteLine("Connected!");

// Get a stream object for reading and writing
NetworkStream stream = client.GetStream();
// Loop to receive all the data sent by the client.
while (true)
{
ReadTemperature(ref stream);
}

//// Shutdown and end connection
//client.Close();
}
}
catch (SocketException e)
{
Console.WriteLine("SocketException: {0}", e);
// Stop listening for new clients.
server.Stop();

}


#endregion
void ReadTemperature(ref NetworkStream networkStream)
{
var temperature = theDs.ReadTemperature().ToString();
Byte sendBytes = Encoding.ASCII.GetBytes(temperature);
networkStream.Write(sendBytes, 0, sendBytes.Length);
Console.WriteLine(temperature);
controller.Write(pin, *1;
ledOn = !ledOn;
}
void DeviceInitialize(ref GpioController theController, int ControlPin, ref OneWireThermometerDevice theDal)
{
string busID = "", devieID = "";
theController.OpenPin(ControlPin, PinMode.Output);
foreach (var device in OneWireThermometerDevice.EnumerateDevices())
{
busID = device.BusId;
devieID = device.DeviceId;
Console.WriteLine(busID + "," + devieID + ","+device.Family);
}
theDal = new OneWireThermometerDevice(busID, devieID);

}
}

}
}

 

SCREENSHOOT:

left: recieved data right: sent data

f:id:sesnail:20210206133758p:plain



 

 

*1:ledOn) ? PinValue.High : PinValue.Low

.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"

.net Framwork4.7.2 & VB.net でML.NETを試してみた(3)- モデルのトレーニングと画像認識

ML.NETの転移学習のチュートリアルを参照して、VB.NETで実装してみます。

docs.microsoft.com

 

データの準備

前回の続き、プロジェクトの直下にフォルダ「assets」、「workspace」を作成し、以下のサイトからダウンロードしたデータを解凍し、「CD」、「UD」フォルダを「assets」にコピーする。

「assets」:トレーニングデータ

「workspace」:トレーニングモデルのデータなど

digitalcommons.usu.edu

f:id:sesnail:20200925183820p:plain

training Data Download

SDNET2018 is an image dataset that contains annotations for cracked and non-cracked concrete structures (bridge decks, walls, and pavement).

The data is organized in three subdirectories:

  • D contains bridge deck images
  • P contains pavement images
  • W contains wall images

Each of these subdirectories contains two additional prefixed subdirectories:

  • C is the prefix used for cracked surfaces
  • U is the prefix used for uncracked surfaces

In this sample, only bridge deck images are used.

 

 

ダブルクリックコピペ

Formをダブルクリック、記事最後に載っているコードを作成しました。詳細はML.NETの転移学習のチュートリアルを参照して、より詳しい解説が載っています。

f:id:sesnail:20200925174952p:plain

Form画像

 

早速実行して見ると、ビルドできませんでした。

これはCPUターゲットを間違えたためです。x64のCPUターゲットを作成し、もう一回実行する。

f:id:sesnail:20200925185306p:plain

CPUターゲットを変更

f:id:sesnail:20200925185350p:plain

64bit CPUターゲットを作成

問題なく動いています。

出力のところに結構スレッド何とか終了しましたなどがあるが、それはトレーニングを加速するためにマルチスレッドを使っていると思います。

 

モデルを訓練するため、2、3分ぐらいかかります。

予測は時々NG

レーニング時間が足らないかもしれません。また調べてみます。

 

レーニングモデルができたみたいですが、毎回のトレーニングすると大変です。

次回、モデルの保存、読み込むなどをやってみます。

 

f:id:sesnail:20200925185033p:plain

実行中

f:id:sesnail:20200925190707p:plain

間違えた予測結果

 

f:id:sesnail:20200925191100p:plain

レーニングした後の「workspace」フォルダ



 

 

Imports System
Imports System.Collections.Generic
Imports System.Linq
Imports System.IO
Imports Microsoft.ML
Imports Microsoft.ML.Data
Imports Microsoft.ML.DataOperationsCatalog
Imports Microsoft.ML.Vision
Imports Microsoft.ML.Transforms
Imports Tensorflow.Data

 

Public Class Form1
#Region "Machine Learning"

 

'画像データクラス
Class ImageData
Public ImagePath As String
Public Label 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

 

'出力データクラス
Class ModelOutput
Public ImagePath As String
Public Label As String
Public PredictedLabel As String
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
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 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

 

'1 枚の画像を分類する
Public Sub ClassifySingleImage(ByVal themlContext As MLContext, ByVal data As IDataView, ByVal trainModel As ITransformer)
Dim predictionEngine As PredictionEngine(Of ModelInput, ModelOutput) = themlContext.Model.CreatePredictionEngine(Of ModelInput, ModelOutput)(trainedModel)
Dim image As ModelInput = themlContext.Data.CreateEnumerable(Of ModelInput)(data, True).First()
Dim prediction As ModelOutput = predictionEngine.Predict(image)
Console.WriteLine("Classifying single image")
OutputPrediction(prediction)


End Sub
#End Region

Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load

 

'データを準備する
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)


'画像のテスト セットを利用して ClassifySingleImage を呼び出します
ClassifySingleImage(theMLContext, testSet, trainedModel)
End Sub


End Class

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

.net Framwork4.7.2 & VB.net でML.NETを試してみた(2)- プロジェクト作成とパッケージのインストール

ML.NETを導入する時は便利なツールがあります。それはML.NET Model Builderです。

クリックだけで、train modelを作れる、コードまで自動的に生成する。

docs.microsoft.com

 

ただ、VB.NETがサポートされていないです。そこで、ML.NETのAPIを使って画像認識を行います。

 

プロジェクトの作成とパッケージのインストール

今回は転移学習のチュートリアルを参照して、VB.netに書き換えてみます。

docs.microsoft.com

 

プロジェクトの作成は結構簡単です。

    1. 普通のVB.netの .net Framwork4.6.1以降のデスクトップアプリプロジェクトを作成します。
    2. 「プロジェクト」の「Nugetパッケージの管理」を開きます。

      f:id:sesnail:20200915214142p:plain

    3. チュートリアルによると、
      • [参照] タブを選択します。
      • [プレリリースを含める] チェックボックスをオンにします。
      • Microsoft.ML を探します。
      • [インストール] ボタンを選択します。
      • [変更のプレビュー] ダイアログの [OK] を選択します。表示されているパッケージのライセンス条項に同意する場合は、 [ライセンスの同意] ダイアログの [同意する] を選択します。
      • Microsoft.ML.VisionSciSharp.TensorFlow.Redist、および Microsoft.ML.ImageAnalytics NuGet パッケージに対して、これらの手順を繰り返します。

       各パッケージのインストールに伴い、サポートパッケージインストールされますので、たくさんのパッケージが表示されます。全部インストールできたら以下のようになります。(パッケージのインストールがとても大事で、詳細の紹介はMicrosoftチュートリアルを参照してください。)f:id:sesnail:20200915220707p:plain

転移学習

 簡単に言うとTensorflowですでにレーニングしたモデルを用いて、自分のデータと合わせてトレーニングし、モデルを特化する学習するプログレスです。

利点としては、モデルの学習時間が短縮できます。

欠点は、事前トレーニング済みモデルなので、不要データが入っているので、データが大きい。

 

 

 

.net Framwork4.7.2 & VB.net でML.NETを試してみた(1)- 実行環境の紹介

最近ML.NETを使ってみました。ネット上検索してみたら、たくさん紹介記事が出てくると思いますが、基本C#コードのMicrosoftチュートリアルの紹介です。

 

私の会社では基本VBは主なる言語で、VB.NETC#が似てると言っても、VBC#に変えることがなかなかできなそもないです。

 

そこで、ML.NETがVB.NETで使えるかどうかを調べました。

 

ML.NETの実行環境

 

Microsoftの紹介ページによると、ML.NETがC#、F#をサポートしています。

dotnet.microsoft.com

 

また、チュートリアルのサンプルプロジェクトは .NET Coreでないとダメ見たいです。

dotnet.microsoft.com

 

しかし、ML. NETのGitHubページを見ると .NET Frameworkもサポートされています。

f:id:sesnail:20200914231001p:plain

ML.NETのGitHubの紹介

さらに、気になった画像認識の機能使えるかどうかを質問したところ、OKの返事も来ました。

f:id:sesnail:20200914231710p:plain