はじめに
Pythonスクリプトの実行結果(数字)をリアルタイムにWPFで作ったWindowに表示させることが目的です。Webカメラからの映像をPython内のOpenCVを使って右側に表示しています。Python内でボトルの数を数え、その結果をC#にリアルタイムに送信しています。
左側のWindowはWPFで作ったものでボトルの本数が表示されています。
静止画で分かりにくいですがWebカメラに映りこむボトルの本数が変わればWPFのWindowの表示がリアルタイムに変化します。
Pythonでやったことは過去の2記事を参照して下さい。
- 【Realtime Object Detection】動画に対して物体検出(ssd_512_mobilenet1.0) - パソコン関連もろもろ
- 【MXNet】写真に写っている人を数える(Faster RCNN resnet101を用いた物体検出) - パソコン関連もろもろ
環境
Windows 11 Core i7-7700K + GTX 1080 Visual Studio Community 2019 .NET 5.0 Python 3.7.9
autocfg==0.0.8 certifi==2021.5.30 chardet==3.0.4 colorama==0.4.4 cycler==0.10.0 gluoncv==0.10.4.post4 graphviz==0.8.4 idna==2.6 kiwisolver==1.3.2 matplotlib==3.4.3 mxnet-cu102==1.7.0 numpy==1.21.2 opencv-python==4.5.3.56 pandas==1.3.3 Pillow==8.3.2 portalocker==2.3.2 pyparsing==2.4.7 python-dateutil==2.8.2 pytz==2021.3 pywin32==301 PyYAML==5.4.1 requests==2.18.4 scipy==1.7.1 six==1.16.0 tqdm==4.62.3 urllib3==1.22 yacs==0.1.8
Python仮想環境にインストールしたのは「mxnet」と「gluoncv」のみです。二つをインストールすると他はついてきます。
インストール方法はこちらを参照して下さい。
GPUがないと動作がカクカクになります。もっと軽い物体検出モデルを選択すれば問題ないと思います。
Pythonスクリプト
特別な書き換えは必要ありません。カウント結果をC#に送るのは以下の1行です。print(count)
全体のスクリプトは以下のようになっています。
import cv2 import mxnet as mx from gluoncv import model_zoo, data, utils ctx = mx.gpu() if mx.context.num_gpus() >0 else mx.cpu() cap = cv2.VideoCapture(0, cv2.CAP_DSHOW) net = model_zoo.get_model('faster_rcnn_fpn_resnet101_v1d_coco', pretrained=True, root='./models', ctx=ctx) net.reset_class(['bottle'], reuse_weights=['bottle']) while True: ret, frame = cap.read() frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) mx_image = mx.nd.array(frame_rgb) x, img = data.transforms.presets.rcnn.transform_test(mx_image, short=480) class_IDs, scores, bounding_boxs = net(x.as_in_context(ctx)) count = int(mx.nd.sum(scores[0]>0.8).asscalar()) print(count) display_img = utils.viz.cv_plot_bbox(img, bounding_boxs[0], scores[0], class_IDs[0], class_names=net.classes) utils.viz.cv_plot_image(display_img) if cv2.waitKey(1) & 0xFF == 27: break cap.release() cv2.destroyAllWindows()
C#コード
今回もXAMLは記述していません。「WindowSettings.cs」内にGUI部分を記述しています。MainWindow.xaml.cs
今回初めて「Dispatcher」について勉強しました。C#側からPythonを呼び出すときになぜか「-u」オプションをつけることが必要です。この部分でだいぶはまりました。
using System; using System.Diagnostics; using System.Windows; namespace WpfApp1 { public partial class MainWindow : Window { Process myProcess = new Process(); public MainWindow() { InitializeComponent(); WindowSettings(); } private void PushStartButton(object sender, RoutedEventArgs e) { if (String.IsNullOrEmpty(selectedPythonPath)) return; PythonStart.IsEnabled = false; PythonStop.IsEnabled = true; string myPythonApp = "-u cv.py"; myProcess = new Process { StartInfo = new ProcessStartInfo(selectedPythonPath) { UseShellExecute = false, RedirectStandardOutput = true, CreateNoWindow = true, Arguments = myPythonApp } }; myProcess.OutputDataReceived += DataReceive; myProcess.Start(); myProcess.BeginOutputReadLine(); } private void PushStopButton(object sender, RoutedEventArgs e) { myProcess.Kill(); PythonStop.IsEnabled = false; PythonStart.IsEnabled = true; } private void DataReceive(object sender, DataReceivedEventArgs e) { if(e != null && e.Data !=null && e.Data.Length > 0) { Dispatcher.Invoke((Action)(() => { resultLabel.Content = e.Data; })); } } } }
WindowSettings.cs
using Microsoft.Win32; using System.Windows; using System.Windows.Controls; namespace WpfApp1 { public partial class MainWindow : Window { string selectedPythonPath; Label resultLabel = new Label() { Content = "0" }; Button PythonStart = new() { Content = new Viewbox() { Child = new Label() { Content = "Start Python" } }, Margin = new Thickness(10) }; Button PythonStop = new() { Content = new Viewbox() { Child = new Label() { Content = "Stop Python" } }, Margin = new Thickness(10), IsEnabled = false }; Button selectPythonButton = new Button() { Content = new Viewbox() { Child = new Label() { Content = "Select Python Interpreter" } }, Margin = new Thickness(5) }; Label selectedPythonLabel = new Label() { Content = "", VerticalAlignment = VerticalAlignment.Center, HorizontalAlignment = HorizontalAlignment.Left }; private void WindowSettings() { selectPythonButton.Click += PushselectPythonButton; PythonStart.Click += PushStartButton; PythonStop.Click += PushStopButton; Grid mainGrid = Myfunc.NewGrid("1,8,1", ""); Content = mainGrid; Grid upperGrid = Myfunc.NewGrid("", "3,7"); upperGrid.AddChild(selectPythonButton, 0, 0, 1, 1); upperGrid.AddChild(selectedPythonLabel, 0, 1, 1, 1); Grid lowerGrid = Myfunc.NewGrid("", "1,1"); Viewbox vb = new Viewbox() { Child = resultLabel, Margin = new Thickness(5) }; lowerGrid.AddChild(vb, 0, 0, 1, 1); mainGrid.AddChild(upperGrid, 0, 0, 1, 1); mainGrid.AddChild(lowerGrid, 1, 0, 1, 1); Grid buttonGrid = Myfunc.NewGrid("1,1", ""); lowerGrid.AddChild(buttonGrid, 0, 1, 1, 1); buttonGrid.AddChild(PythonStart, 0, 0, 1, 1); buttonGrid.AddChild(PythonStop, 1, 0, 1, 1); } private void PushselectPythonButton(object sender, RoutedEventArgs e) { var dialog = new OpenFileDialog(); dialog.Filter = "Python実行ファイル (*.exe)|*.exe"; if (dialog.ShowDialog() == true) { selectedPythonPath = dialog.FileName; selectedPythonLabel.Content = selectedPythonPath; } } } }
MyFunc.cs
Gridを作成してChildrenを追加するときに役に立つメソッドです。using System.Windows; using System.Windows.Controls; static class Myfunc { public static Grid NewGrid(string row, string col) //引数はカンマ区切りで { Grid grid = new Grid(); string[] grid_row = row.Split(","); string[] grid_col = col.Split(","); if (grid_row.Length > 1) { foreach (string each in grid_row) { if (int.TryParse(each, out int each_num)) { grid.RowDefinitions.Add(new RowDefinition() { Height = new GridLength(each_num, GridUnitType.Star) }); } else { grid.RowDefinitions.Add(new RowDefinition() { Height = GridLength.Auto }); } } } if (grid_col.Length > 1) { foreach (string each in grid_col) { if (int.TryParse(each, out int each_num)) { grid.ColumnDefinitions.Add(new ColumnDefinition() { Width = new GridLength(each_num, GridUnitType.Star) }); } else { grid.ColumnDefinitions.Add(new ColumnDefinition() { Width = GridLength.Auto }); } } } return grid; } public static Grid AddChild(this Grid grid, UIElement element, int row, int col, int row_span, int col_span) { Grid.SetRow(element, row); Grid.SetRowSpan(element, row_span); Grid.SetColumn(element, col); Grid.SetColumnSpan(element, col_span); grid.Children.Add(element); return grid; } }