├── .gitignore ├── README.md ├── assets ├── model │ └── 2d57957ce10f4489a43725da328a5ba9.ONNX.zip ├── test │ ├── maksssksksss348.png │ ├── maksssksksss432.png │ ├── maksssksksss468.png │ ├── maksssksksss490.png │ ├── maksssksksss504.png │ ├── maksssksksss528.png │ ├── maksssksksss530.png │ ├── maksssksksss531.png │ ├── maksssksksss532.png │ ├── maksssksksss537.png │ ├── maksssksksss561.png │ ├── maksssksksss565.png │ ├── maksssksksss571.png │ └── maksssksksss598.png └── train │ ├── maksssksksss0.png │ ├── maksssksksss10.png │ ├── maksssksksss126.png │ ├── maksssksksss128.png │ ├── maksssksksss162.png │ ├── maksssksksss180.png │ ├── maksssksksss188.png │ ├── maksssksksss210.png │ ├── maksssksksss295.png │ ├── maksssksksss35.png │ ├── maksssksksss37.png │ ├── maksssksksss39.png │ ├── maksssksksss51.png │ ├── maksssksksss6.png │ ├── maksssksksss60.png │ ├── maksssksksss62.png │ ├── maksssksksss72.png │ ├── maksssksksss8.png │ ├── maksssksksss88.png │ └── maksssksksss96.png ├── customvision-train ├── README.md ├── customvision-1.jpg ├── customvision-2.jpg ├── customvision-3.jpg ├── customvision-4.jpg ├── customvision-5.jpg ├── customvision-6.jpg ├── customvision-export-1.jpg ├── customvision-export-2.jpg ├── customvision-test-1.jpg ├── customvision-test-2.jpg ├── customvision-test-3.jpg ├── customvision-test-4.jpg ├── customvision-test-5.jpg ├── customvision-train-1.jpg └── customvision-train-2.jpg ├── images ├── assets-test.jpg ├── assets-train.jpg ├── labelstudio-image.jpg ├── makesense-image.jpg ├── showcase.gif └── vott-image.jpg └── src ├── MachineLearning.ObjectDetect.WPF.sln ├── MachineLearning.ObjectDetect.WPF ├── App.xaml ├── App.xaml.cs ├── ColorExtensions.cs ├── ControlStyles │ └── ToggleSwitch.xaml ├── FodyWeavers.xml ├── FodyWeavers.xsd ├── MachineLearning.ObjectDetect.WPF.csproj ├── OnnxModels │ ├── TinyYolo2_model.onnx │ └── facemask.ONNX.zip ├── ReactiveMetroWindow.cs ├── Services │ └── CameraOpenCv.cs ├── TestImages │ ├── 00_maksssksksss598.png │ ├── 01_maksssksksss348.png │ ├── 02_maksssksksss531.png │ ├── maksssksksss432.png │ ├── maksssksksss490.png │ ├── maksssksksss504.png │ ├── maksssksksss528.png │ ├── maksssksksss530.png │ ├── maksssksksss532.png │ ├── maksssksksss537.png │ ├── maksssksksss561.png │ ├── maksssksksss565.png │ └── maksssksksss571.png ├── ViewModels │ ├── FolderViewModel.cs │ ├── MainWindowViewModel.cs │ ├── SelectViewModel.cs │ └── WebcamViewModel.cs └── Views │ ├── FolderView.xaml │ ├── FolderView.xaml.cs │ ├── MainWindow.xaml │ ├── MainWindow.xaml.cs │ ├── SelectView.xaml │ ├── SelectView.xaml.cs │ ├── WebcamView.xaml │ └── WebcamView.xaml.cs └── OnnxObjectDetection ├── BoundingBox.cs ├── BoundingBoxDimensions.cs ├── ML ├── DataModels │ ├── CustomVisionModel.cs │ ├── CustomVisionPrediction.cs │ ├── IOnnxModel.cs │ ├── ImageInputData.cs │ ├── TinyYoloModel.cs │ └── TinyYoloPrediction.cs └── OnnxModelConfigurator.cs ├── OnnxObjectDetection.csproj └── OnnxOutputParser.cs /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.rsuser 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Mono auto generated files 17 | mono_crash.* 18 | 19 | # Build results 20 | [Dd]ebug/ 21 | [Dd]ebugPublic/ 22 | [Rr]elease/ 23 | [Rr]eleases/ 24 | x64/ 25 | x86/ 26 | [Aa][Rr][Mm]/ 27 | [Aa][Rr][Mm]64/ 28 | bld/ 29 | [Bb]in/ 30 | [Oo]bj/ 31 | [Ll]og/ 32 | [Ll]ogs/ 33 | 34 | # Visual Studio 2015/2017 cache/options directory 35 | .vs/ 36 | # Uncomment if you have tasks that create the project's static files in wwwroot 37 | #wwwroot/ 38 | 39 | # Visual Studio 2017 auto generated files 40 | Generated\ Files/ 41 | 42 | # MSTest test Results 43 | [Tt]est[Rr]esult*/ 44 | [Bb]uild[Ll]og.* 45 | 46 | # NUnit 47 | *.VisualState.xml 48 | TestResult.xml 49 | nunit-*.xml 50 | 51 | # Build Results of an ATL Project 52 | [Dd]ebugPS/ 53 | [Rr]eleasePS/ 54 | dlldata.c 55 | 56 | # Benchmark Results 57 | BenchmarkDotNet.Artifacts/ 58 | 59 | # .NET Core 60 | project.lock.json 61 | project.fragment.lock.json 62 | artifacts/ 63 | 64 | # StyleCop 65 | StyleCopReport.xml 66 | 67 | # Files built by Visual Studio 68 | *_i.c 69 | *_p.c 70 | *_h.h 71 | *.ilk 72 | *.meta 73 | *.obj 74 | *.iobj 75 | *.pch 76 | *.pdb 77 | *.ipdb 78 | *.pgc 79 | *.pgd 80 | *.rsp 81 | *.sbr 82 | *.tlb 83 | *.tli 84 | *.tlh 85 | *.tmp 86 | *.tmp_proj 87 | *_wpftmp.csproj 88 | *.log 89 | *.vspscc 90 | *.vssscc 91 | .builds 92 | *.pidb 93 | *.svclog 94 | *.scc 95 | 96 | # Chutzpah Test files 97 | _Chutzpah* 98 | 99 | # Visual C++ cache files 100 | ipch/ 101 | *.aps 102 | *.ncb 103 | *.opendb 104 | *.opensdf 105 | *.sdf 106 | *.cachefile 107 | *.VC.db 108 | *.VC.VC.opendb 109 | 110 | # Visual Studio profiler 111 | *.psess 112 | *.vsp 113 | *.vspx 114 | *.sap 115 | 116 | # Visual Studio Trace Files 117 | *.e2e 118 | 119 | # TFS 2012 Local Workspace 120 | $tf/ 121 | 122 | # Guidance Automation Toolkit 123 | *.gpState 124 | 125 | # ReSharper is a .NET coding add-in 126 | _ReSharper*/ 127 | *.[Rr]e[Ss]harper 128 | *.DotSettings.user 129 | 130 | # TeamCity is a build add-in 131 | _TeamCity* 132 | 133 | # DotCover is a Code Coverage Tool 134 | *.dotCover 135 | 136 | # AxoCover is a Code Coverage Tool 137 | .axoCover/* 138 | !.axoCover/settings.json 139 | 140 | # Visual Studio code coverage results 141 | *.coverage 142 | *.coveragexml 143 | 144 | # NCrunch 145 | _NCrunch_* 146 | .*crunch*.local.xml 147 | nCrunchTemp_* 148 | 149 | # MightyMoose 150 | *.mm.* 151 | AutoTest.Net/ 152 | 153 | # Web workbench (sass) 154 | .sass-cache/ 155 | 156 | # Installshield output folder 157 | [Ee]xpress/ 158 | 159 | # DocProject is a documentation generator add-in 160 | DocProject/buildhelp/ 161 | DocProject/Help/*.HxT 162 | DocProject/Help/*.HxC 163 | DocProject/Help/*.hhc 164 | DocProject/Help/*.hhk 165 | DocProject/Help/*.hhp 166 | DocProject/Help/Html2 167 | DocProject/Help/html 168 | 169 | # Click-Once directory 170 | publish/ 171 | 172 | # Publish Web Output 173 | *.[Pp]ublish.xml 174 | *.azurePubxml 175 | # Note: Comment the next line if you want to checkin your web deploy settings, 176 | # but database connection strings (with potential passwords) will be unencrypted 177 | *.pubxml 178 | *.publishproj 179 | 180 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 181 | # checkin your Azure Web App publish settings, but sensitive information contained 182 | # in these scripts will be unencrypted 183 | PublishScripts/ 184 | 185 | # NuGet Packages 186 | *.nupkg 187 | # NuGet Symbol Packages 188 | *.snupkg 189 | # The packages folder can be ignored because of Package Restore 190 | **/[Pp]ackages/* 191 | # except build/, which is used as an MSBuild target. 192 | !**/[Pp]ackages/build/ 193 | # Uncomment if necessary however generally it will be regenerated when needed 194 | #!**/[Pp]ackages/repositories.config 195 | # NuGet v3's project.json files produces more ignorable files 196 | *.nuget.props 197 | *.nuget.targets 198 | 199 | # Microsoft Azure Build Output 200 | csx/ 201 | *.build.csdef 202 | 203 | # Microsoft Azure Emulator 204 | ecf/ 205 | rcf/ 206 | 207 | # Windows Store app package directories and files 208 | AppPackages/ 209 | BundleArtifacts/ 210 | Package.StoreAssociation.xml 211 | _pkginfo.txt 212 | *.appx 213 | *.appxbundle 214 | *.appxupload 215 | 216 | # Visual Studio cache files 217 | # files ending in .cache can be ignored 218 | *.[Cc]ache 219 | # but keep track of directories ending in .cache 220 | !?*.[Cc]ache/ 221 | 222 | # Others 223 | ClientBin/ 224 | ~$* 225 | *~ 226 | *.dbmdl 227 | *.dbproj.schemaview 228 | *.jfm 229 | *.pfx 230 | *.publishsettings 231 | orleans.codegen.cs 232 | 233 | # Including strong name files can present a security risk 234 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 235 | #*.snk 236 | 237 | # Since there are multiple workflows, uncomment next line to ignore bower_components 238 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 239 | #bower_components/ 240 | 241 | # RIA/Silverlight projects 242 | Generated_Code/ 243 | 244 | # Backup & report files from converting an old project file 245 | # to a newer Visual Studio version. Backup files are not needed, 246 | # because we have git ;-) 247 | _UpgradeReport_Files/ 248 | Backup*/ 249 | UpgradeLog*.XML 250 | UpgradeLog*.htm 251 | ServiceFabricBackup/ 252 | *.rptproj.bak 253 | 254 | # SQL Server files 255 | *.mdf 256 | *.ldf 257 | *.ndf 258 | 259 | # Business Intelligence projects 260 | *.rdl.data 261 | *.bim.layout 262 | *.bim_*.settings 263 | *.rptproj.rsuser 264 | *- [Bb]ackup.rdl 265 | *- [Bb]ackup ([0-9]).rdl 266 | *- [Bb]ackup ([0-9][0-9]).rdl 267 | 268 | # Microsoft Fakes 269 | FakesAssemblies/ 270 | 271 | # GhostDoc plugin setting file 272 | *.GhostDoc.xml 273 | 274 | # Node.js Tools for Visual Studio 275 | .ntvs_analysis.dat 276 | node_modules/ 277 | 278 | # Visual Studio 6 build log 279 | *.plg 280 | 281 | # Visual Studio 6 workspace options file 282 | *.opt 283 | 284 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 285 | *.vbw 286 | 287 | # Visual Studio LightSwitch build output 288 | **/*.HTMLClient/GeneratedArtifacts 289 | **/*.DesktopClient/GeneratedArtifacts 290 | **/*.DesktopClient/ModelManifest.xml 291 | **/*.Server/GeneratedArtifacts 292 | **/*.Server/ModelManifest.xml 293 | _Pvt_Extensions 294 | 295 | # Paket dependency manager 296 | .paket/paket.exe 297 | paket-files/ 298 | 299 | # FAKE - F# Make 300 | .fake/ 301 | 302 | # CodeRush personal settings 303 | .cr/personal 304 | 305 | # Python Tools for Visual Studio (PTVS) 306 | __pycache__/ 307 | *.pyc 308 | 309 | # Cake - Uncomment if you are using it 310 | # tools/** 311 | # !tools/packages.config 312 | 313 | # Tabs Studio 314 | *.tss 315 | 316 | # Telerik's JustMock configuration file 317 | *.jmconfig 318 | 319 | # BizTalk build output 320 | *.btp.cs 321 | *.btm.cs 322 | *.odx.cs 323 | *.xsd.cs 324 | 325 | # OpenCover UI analysis results 326 | OpenCover/ 327 | 328 | # Azure Stream Analytics local run output 329 | ASALocalRun/ 330 | 331 | # MSBuild Binary and Structured Log 332 | *.binlog 333 | 334 | # NVidia Nsight GPU debugger configuration file 335 | *.nvuser 336 | 337 | # MFractors (Xamarin productivity tool) working folder 338 | .mfractor/ 339 | 340 | # Local History for Visual Studio 341 | .localhistory/ 342 | 343 | # BeatPulse healthcheck temp database 344 | healthchecksdb 345 | 346 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 347 | MigrationBackup/ 348 | 349 | # Ionide (cross platform F# VS Code tools) working folder 350 | .ionide/ 351 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Machine Learning Object Detection in .NET with WPF App 2 | Complete sample for object detection in .NET using Machine Learning and WPF. From image labeling to training to final app. 3 | 4 | > For sample/demonstration/understanding of the full cycle I will label and train with just a few images of a very simple problem. Labeling and model training depending the domain and complexity of the project is something best done by a Data Scientist specialist. 5 | For .NET developers I think the important part is Part 3 so they can integrate with their software and add value. Labeling and model training can be outsource to a specialist or use one of the several public models available made by experts. 6 | 7 | ![](images/showcase.gif) 8 | 9 | ## Part 1 - Labeling 10 | The first step to detect objects in images/video is to label them. There are several applications and websites for this. Opensource, comercial, local only, cloud backend to fit everyone needs/taste. Here is a short list of some of them: 11 | 12 | 13 | * [VoTT - Microsoft Visual Object Tagging Tool](https://github.com/microsoft/VoTT) 14 | 15 | Can be run as a desktop application and works with local files or directly [online](https://vott.z22.web.core.windows.net/) using Azure Blob Storage. It allows to export to several formats (Pascal VOC, TensorFlow recods, Azure Custom Vision Service, etc) 16 | 17 | ![](images/vott-image.jpg) 18 | 19 | 20 | * [Azure Custom Vision Portal](https://www.customvision.ai/) 21 | 22 | It's part of Azure Cognitive Services and allows to upload images and label them directly in the web. 23 | 24 | ![](customvision-train/customvision-1.jpg) 25 | 26 | 27 | * [Label Studio](https://labelstud.io/) 28 | 29 | Open Source data labeling tool. Apart from labeling images for object detection it can also work with audio, texts, etc. You can run it locally with Docker in one minute. 30 | 31 | ![](images/labelstudio-image.jpg) 32 | 33 | 34 | * [Make Sense](https://www.makesense.ai/) 35 | 36 | Open Source annotation tool. It runs directly on the web and allows to export the bounding box labels in many formats. 37 | 38 | ![](images/makesense-image.jpg) 39 | 40 | * Comercial options (some with Free Tier) 41 | * [Super Annotate](https://superannotate.com/) 42 | * [Clarifai](https://www.clarifai.com/) 43 | * [Supervisely](https://supervise.ly/) 44 | * [Labelbox](https://labelbox.com/) 45 | 46 | 47 | ## Part 2 - Model training 48 | 49 | * [Azure Custom Vision Portal](https://www.customvision.ai/) 50 | 51 | For this sample I used this simple tool for labeling and training. I used the [facemask dataset](https://www.kaggle.com/sshikamaru/face-mask-detection) from Kaggle and I just used 20 images for training and a few for testing. Labeling took me about two minutes and I did it very rough and model training took just 5 minutes. 52 | 53 | [See more details/pictures steps here](customvision-train) 54 | 55 | ![](customvision-train/customvision-3.jpg) 56 | 57 | * [TensorFlow](https://www.tensorflow.org/) 58 | 59 | ML.NET can work with TensorFlow models or you can convert to [ONNX format](https://onnx.ai/) 60 | 61 | * [PyTorch](https://pytorch.org/) 62 | 63 | If you are comfortable with Python and export to ONNX 64 | 65 | ## Part 3 - WPF Application 66 | In this sample I chose to use a .NET 5 WPF application so that the sample is helpful for .NET desktop developers. Obviously deploying the previous model as a Docker container or a REST API endpoint for web use is also possible and maybe even simpler and they are several samples/tutorials online for this. 67 | 68 | The sample is based on [Microsoft Machine Learning Sample](https://github.com/dotnet/machinelearning-samples/tree/master/samples/csharp/end-to-end-apps/ObjectDetection-Onnx). 69 | 70 | You can check the source code [here](src). As with most of my WPF projects I used MahApps and ReactiveUI but those are not required. 71 | -------------------------------------------------------------------------------- /assets/model/2d57957ce10f4489a43725da328a5ba9.ONNX.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gerardo-lijs/MachineLearning-ObjectDetect-WPF/2e48f55b95031f7abcf012ce3dee20fb2cf9918f/assets/model/2d57957ce10f4489a43725da328a5ba9.ONNX.zip -------------------------------------------------------------------------------- /assets/test/maksssksksss348.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gerardo-lijs/MachineLearning-ObjectDetect-WPF/2e48f55b95031f7abcf012ce3dee20fb2cf9918f/assets/test/maksssksksss348.png -------------------------------------------------------------------------------- /assets/test/maksssksksss432.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gerardo-lijs/MachineLearning-ObjectDetect-WPF/2e48f55b95031f7abcf012ce3dee20fb2cf9918f/assets/test/maksssksksss432.png -------------------------------------------------------------------------------- /assets/test/maksssksksss468.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gerardo-lijs/MachineLearning-ObjectDetect-WPF/2e48f55b95031f7abcf012ce3dee20fb2cf9918f/assets/test/maksssksksss468.png -------------------------------------------------------------------------------- /assets/test/maksssksksss490.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gerardo-lijs/MachineLearning-ObjectDetect-WPF/2e48f55b95031f7abcf012ce3dee20fb2cf9918f/assets/test/maksssksksss490.png -------------------------------------------------------------------------------- /assets/test/maksssksksss504.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gerardo-lijs/MachineLearning-ObjectDetect-WPF/2e48f55b95031f7abcf012ce3dee20fb2cf9918f/assets/test/maksssksksss504.png -------------------------------------------------------------------------------- /assets/test/maksssksksss528.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gerardo-lijs/MachineLearning-ObjectDetect-WPF/2e48f55b95031f7abcf012ce3dee20fb2cf9918f/assets/test/maksssksksss528.png -------------------------------------------------------------------------------- /assets/test/maksssksksss530.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gerardo-lijs/MachineLearning-ObjectDetect-WPF/2e48f55b95031f7abcf012ce3dee20fb2cf9918f/assets/test/maksssksksss530.png -------------------------------------------------------------------------------- /assets/test/maksssksksss531.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gerardo-lijs/MachineLearning-ObjectDetect-WPF/2e48f55b95031f7abcf012ce3dee20fb2cf9918f/assets/test/maksssksksss531.png -------------------------------------------------------------------------------- /assets/test/maksssksksss532.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gerardo-lijs/MachineLearning-ObjectDetect-WPF/2e48f55b95031f7abcf012ce3dee20fb2cf9918f/assets/test/maksssksksss532.png -------------------------------------------------------------------------------- /assets/test/maksssksksss537.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gerardo-lijs/MachineLearning-ObjectDetect-WPF/2e48f55b95031f7abcf012ce3dee20fb2cf9918f/assets/test/maksssksksss537.png -------------------------------------------------------------------------------- /assets/test/maksssksksss561.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gerardo-lijs/MachineLearning-ObjectDetect-WPF/2e48f55b95031f7abcf012ce3dee20fb2cf9918f/assets/test/maksssksksss561.png -------------------------------------------------------------------------------- /assets/test/maksssksksss565.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gerardo-lijs/MachineLearning-ObjectDetect-WPF/2e48f55b95031f7abcf012ce3dee20fb2cf9918f/assets/test/maksssksksss565.png -------------------------------------------------------------------------------- /assets/test/maksssksksss571.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gerardo-lijs/MachineLearning-ObjectDetect-WPF/2e48f55b95031f7abcf012ce3dee20fb2cf9918f/assets/test/maksssksksss571.png -------------------------------------------------------------------------------- /assets/test/maksssksksss598.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gerardo-lijs/MachineLearning-ObjectDetect-WPF/2e48f55b95031f7abcf012ce3dee20fb2cf9918f/assets/test/maksssksksss598.png -------------------------------------------------------------------------------- /assets/train/maksssksksss0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gerardo-lijs/MachineLearning-ObjectDetect-WPF/2e48f55b95031f7abcf012ce3dee20fb2cf9918f/assets/train/maksssksksss0.png -------------------------------------------------------------------------------- /assets/train/maksssksksss10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gerardo-lijs/MachineLearning-ObjectDetect-WPF/2e48f55b95031f7abcf012ce3dee20fb2cf9918f/assets/train/maksssksksss10.png -------------------------------------------------------------------------------- /assets/train/maksssksksss126.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gerardo-lijs/MachineLearning-ObjectDetect-WPF/2e48f55b95031f7abcf012ce3dee20fb2cf9918f/assets/train/maksssksksss126.png -------------------------------------------------------------------------------- /assets/train/maksssksksss128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gerardo-lijs/MachineLearning-ObjectDetect-WPF/2e48f55b95031f7abcf012ce3dee20fb2cf9918f/assets/train/maksssksksss128.png -------------------------------------------------------------------------------- /assets/train/maksssksksss162.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gerardo-lijs/MachineLearning-ObjectDetect-WPF/2e48f55b95031f7abcf012ce3dee20fb2cf9918f/assets/train/maksssksksss162.png -------------------------------------------------------------------------------- /assets/train/maksssksksss180.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gerardo-lijs/MachineLearning-ObjectDetect-WPF/2e48f55b95031f7abcf012ce3dee20fb2cf9918f/assets/train/maksssksksss180.png -------------------------------------------------------------------------------- /assets/train/maksssksksss188.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gerardo-lijs/MachineLearning-ObjectDetect-WPF/2e48f55b95031f7abcf012ce3dee20fb2cf9918f/assets/train/maksssksksss188.png -------------------------------------------------------------------------------- /assets/train/maksssksksss210.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gerardo-lijs/MachineLearning-ObjectDetect-WPF/2e48f55b95031f7abcf012ce3dee20fb2cf9918f/assets/train/maksssksksss210.png -------------------------------------------------------------------------------- /assets/train/maksssksksss295.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gerardo-lijs/MachineLearning-ObjectDetect-WPF/2e48f55b95031f7abcf012ce3dee20fb2cf9918f/assets/train/maksssksksss295.png -------------------------------------------------------------------------------- /assets/train/maksssksksss35.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gerardo-lijs/MachineLearning-ObjectDetect-WPF/2e48f55b95031f7abcf012ce3dee20fb2cf9918f/assets/train/maksssksksss35.png -------------------------------------------------------------------------------- /assets/train/maksssksksss37.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gerardo-lijs/MachineLearning-ObjectDetect-WPF/2e48f55b95031f7abcf012ce3dee20fb2cf9918f/assets/train/maksssksksss37.png -------------------------------------------------------------------------------- /assets/train/maksssksksss39.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gerardo-lijs/MachineLearning-ObjectDetect-WPF/2e48f55b95031f7abcf012ce3dee20fb2cf9918f/assets/train/maksssksksss39.png -------------------------------------------------------------------------------- /assets/train/maksssksksss51.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gerardo-lijs/MachineLearning-ObjectDetect-WPF/2e48f55b95031f7abcf012ce3dee20fb2cf9918f/assets/train/maksssksksss51.png -------------------------------------------------------------------------------- /assets/train/maksssksksss6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gerardo-lijs/MachineLearning-ObjectDetect-WPF/2e48f55b95031f7abcf012ce3dee20fb2cf9918f/assets/train/maksssksksss6.png -------------------------------------------------------------------------------- /assets/train/maksssksksss60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gerardo-lijs/MachineLearning-ObjectDetect-WPF/2e48f55b95031f7abcf012ce3dee20fb2cf9918f/assets/train/maksssksksss60.png -------------------------------------------------------------------------------- /assets/train/maksssksksss62.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gerardo-lijs/MachineLearning-ObjectDetect-WPF/2e48f55b95031f7abcf012ce3dee20fb2cf9918f/assets/train/maksssksksss62.png -------------------------------------------------------------------------------- /assets/train/maksssksksss72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gerardo-lijs/MachineLearning-ObjectDetect-WPF/2e48f55b95031f7abcf012ce3dee20fb2cf9918f/assets/train/maksssksksss72.png -------------------------------------------------------------------------------- /assets/train/maksssksksss8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gerardo-lijs/MachineLearning-ObjectDetect-WPF/2e48f55b95031f7abcf012ce3dee20fb2cf9918f/assets/train/maksssksksss8.png -------------------------------------------------------------------------------- /assets/train/maksssksksss88.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gerardo-lijs/MachineLearning-ObjectDetect-WPF/2e48f55b95031f7abcf012ce3dee20fb2cf9918f/assets/train/maksssksksss88.png -------------------------------------------------------------------------------- /assets/train/maksssksksss96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gerardo-lijs/MachineLearning-ObjectDetect-WPF/2e48f55b95031f7abcf012ce3dee20fb2cf9918f/assets/train/maksssksksss96.png -------------------------------------------------------------------------------- /customvision-train/README.md: -------------------------------------------------------------------------------- 1 | # Microsoft Custom Vision training and test 2 | Some screenshots I made while training to show how simple it is. This is not meant to be a step by step guide, you can find several tutorials online. 3 | 4 | ## Create project 5 | 6 | ![](customvision-1.jpg) 7 |   8 |   9 | 10 | ![](customvision-2.jpg) 11 |   12 |   13 | 14 | ## Label 15 | 16 | ![](customvision-3.jpg) 17 |   18 |   19 | 20 | ![](customvision-4.jpg) 21 |   22 |   23 | 24 | ![](customvision-5.jpg) 25 |   26 |   27 | 28 | ![](customvision-6.jpg) 29 |   30 |   31 | 32 | ## Train 33 | 34 | ![](customvision-train-1.jpg) 35 |   36 |   37 | 38 | ![](customvision-train-2.jpg) 39 |   40 |   41 | 42 | ## Test 43 | 44 | ![](customvision-test-1.jpg) 45 |   46 |   47 | 48 | ![](customvision-test-2.jpg) 49 |   50 |   51 | 52 | ![](customvision-test-3.jpg) 53 |   54 |   55 | 56 | ![](customvision-test-4.jpg) 57 |   58 |   59 | 60 | ![](customvision-test-5.jpg) 61 |   62 |   63 | 64 | ## Export 65 | 66 | ![](customvision-export-1.jpg) 67 |   68 |   69 | 70 | ![](customvision-export-2.jpg) 71 |   72 |   73 | -------------------------------------------------------------------------------- /customvision-train/customvision-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gerardo-lijs/MachineLearning-ObjectDetect-WPF/2e48f55b95031f7abcf012ce3dee20fb2cf9918f/customvision-train/customvision-1.jpg -------------------------------------------------------------------------------- /customvision-train/customvision-2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gerardo-lijs/MachineLearning-ObjectDetect-WPF/2e48f55b95031f7abcf012ce3dee20fb2cf9918f/customvision-train/customvision-2.jpg -------------------------------------------------------------------------------- /customvision-train/customvision-3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gerardo-lijs/MachineLearning-ObjectDetect-WPF/2e48f55b95031f7abcf012ce3dee20fb2cf9918f/customvision-train/customvision-3.jpg -------------------------------------------------------------------------------- /customvision-train/customvision-4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gerardo-lijs/MachineLearning-ObjectDetect-WPF/2e48f55b95031f7abcf012ce3dee20fb2cf9918f/customvision-train/customvision-4.jpg -------------------------------------------------------------------------------- /customvision-train/customvision-5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gerardo-lijs/MachineLearning-ObjectDetect-WPF/2e48f55b95031f7abcf012ce3dee20fb2cf9918f/customvision-train/customvision-5.jpg -------------------------------------------------------------------------------- /customvision-train/customvision-6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gerardo-lijs/MachineLearning-ObjectDetect-WPF/2e48f55b95031f7abcf012ce3dee20fb2cf9918f/customvision-train/customvision-6.jpg -------------------------------------------------------------------------------- /customvision-train/customvision-export-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gerardo-lijs/MachineLearning-ObjectDetect-WPF/2e48f55b95031f7abcf012ce3dee20fb2cf9918f/customvision-train/customvision-export-1.jpg -------------------------------------------------------------------------------- /customvision-train/customvision-export-2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gerardo-lijs/MachineLearning-ObjectDetect-WPF/2e48f55b95031f7abcf012ce3dee20fb2cf9918f/customvision-train/customvision-export-2.jpg -------------------------------------------------------------------------------- /customvision-train/customvision-test-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gerardo-lijs/MachineLearning-ObjectDetect-WPF/2e48f55b95031f7abcf012ce3dee20fb2cf9918f/customvision-train/customvision-test-1.jpg -------------------------------------------------------------------------------- /customvision-train/customvision-test-2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gerardo-lijs/MachineLearning-ObjectDetect-WPF/2e48f55b95031f7abcf012ce3dee20fb2cf9918f/customvision-train/customvision-test-2.jpg -------------------------------------------------------------------------------- /customvision-train/customvision-test-3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gerardo-lijs/MachineLearning-ObjectDetect-WPF/2e48f55b95031f7abcf012ce3dee20fb2cf9918f/customvision-train/customvision-test-3.jpg -------------------------------------------------------------------------------- /customvision-train/customvision-test-4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gerardo-lijs/MachineLearning-ObjectDetect-WPF/2e48f55b95031f7abcf012ce3dee20fb2cf9918f/customvision-train/customvision-test-4.jpg -------------------------------------------------------------------------------- /customvision-train/customvision-test-5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gerardo-lijs/MachineLearning-ObjectDetect-WPF/2e48f55b95031f7abcf012ce3dee20fb2cf9918f/customvision-train/customvision-test-5.jpg -------------------------------------------------------------------------------- /customvision-train/customvision-train-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gerardo-lijs/MachineLearning-ObjectDetect-WPF/2e48f55b95031f7abcf012ce3dee20fb2cf9918f/customvision-train/customvision-train-1.jpg -------------------------------------------------------------------------------- /customvision-train/customvision-train-2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gerardo-lijs/MachineLearning-ObjectDetect-WPF/2e48f55b95031f7abcf012ce3dee20fb2cf9918f/customvision-train/customvision-train-2.jpg -------------------------------------------------------------------------------- /images/assets-test.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gerardo-lijs/MachineLearning-ObjectDetect-WPF/2e48f55b95031f7abcf012ce3dee20fb2cf9918f/images/assets-test.jpg -------------------------------------------------------------------------------- /images/assets-train.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gerardo-lijs/MachineLearning-ObjectDetect-WPF/2e48f55b95031f7abcf012ce3dee20fb2cf9918f/images/assets-train.jpg -------------------------------------------------------------------------------- /images/labelstudio-image.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gerardo-lijs/MachineLearning-ObjectDetect-WPF/2e48f55b95031f7abcf012ce3dee20fb2cf9918f/images/labelstudio-image.jpg -------------------------------------------------------------------------------- /images/makesense-image.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gerardo-lijs/MachineLearning-ObjectDetect-WPF/2e48f55b95031f7abcf012ce3dee20fb2cf9918f/images/makesense-image.jpg -------------------------------------------------------------------------------- /images/showcase.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gerardo-lijs/MachineLearning-ObjectDetect-WPF/2e48f55b95031f7abcf012ce3dee20fb2cf9918f/images/showcase.gif -------------------------------------------------------------------------------- /images/vott-image.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gerardo-lijs/MachineLearning-ObjectDetect-WPF/2e48f55b95031f7abcf012ce3dee20fb2cf9918f/images/vott-image.jpg -------------------------------------------------------------------------------- /src/MachineLearning.ObjectDetect.WPF.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.31019.35 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MachineLearning.ObjectDetect.WPF", "MachineLearning.ObjectDetect.WPF\MachineLearning.ObjectDetect.WPF.csproj", "{67D5581D-339D-4C12-AC3A-7B69FA3DB876}" 7 | EndProject 8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OnnxObjectDetection", "OnnxObjectDetection\OnnxObjectDetection.csproj", "{63A49E42-E567-434D-AC0C-BA8AC185F994}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|Any CPU = Debug|Any CPU 13 | Release|Any CPU = Release|Any CPU 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {67D5581D-339D-4C12-AC3A-7B69FA3DB876}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 17 | {67D5581D-339D-4C12-AC3A-7B69FA3DB876}.Debug|Any CPU.Build.0 = Debug|Any CPU 18 | {67D5581D-339D-4C12-AC3A-7B69FA3DB876}.Release|Any CPU.ActiveCfg = Release|Any CPU 19 | {67D5581D-339D-4C12-AC3A-7B69FA3DB876}.Release|Any CPU.Build.0 = Release|Any CPU 20 | {63A49E42-E567-434D-AC0C-BA8AC185F994}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 21 | {63A49E42-E567-434D-AC0C-BA8AC185F994}.Debug|Any CPU.Build.0 = Debug|Any CPU 22 | {63A49E42-E567-434D-AC0C-BA8AC185F994}.Release|Any CPU.ActiveCfg = Release|Any CPU 23 | {63A49E42-E567-434D-AC0C-BA8AC185F994}.Release|Any CPU.Build.0 = Release|Any CPU 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | GlobalSection(ExtensibilityGlobals) = postSolution 29 | SolutionGuid = {6BE19C12-AFE7-4F70-A694-6D68C817F698} 30 | EndGlobalSection 31 | EndGlobal 32 | -------------------------------------------------------------------------------- /src/MachineLearning.ObjectDetect.WPF/App.xaml: -------------------------------------------------------------------------------- 1 |  9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /src/MachineLearning.ObjectDetect.WPF/App.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Windows; 3 | using Microsoft.Extensions.DependencyInjection; 4 | using Microsoft.Extensions.Hosting; 5 | 6 | using ControlzEx.Theming; 7 | using ReactiveUI; 8 | using Splat; 9 | using Splat.Microsoft.Extensions.DependencyInjection; 10 | 11 | namespace MachineLearning.ObjectDetect.WPF 12 | { 13 | public partial class App : Application 14 | { 15 | public IHost AppHost { get; } 16 | 17 | public App() 18 | { 19 | // Build AppHost 20 | AppHost = Host 21 | .CreateDefaultBuilder() 22 | .ConfigureServices(ConfigureServices) 23 | .Build(); 24 | } 25 | 26 | private async void Application_Startup(object sender, StartupEventArgs e) 27 | { 28 | await AppHost.StartAsync(); 29 | 30 | // Theme 31 | var useWindowsTheme = false; 32 | if (useWindowsTheme) 33 | { 34 | // Use Windows theme 35 | ThemeManager.Current.ThemeSyncMode = ThemeSyncMode.SyncWithAppMode; 36 | ThemeManager.Current.SyncTheme(); 37 | } 38 | else 39 | { 40 | // Default Dark 41 | ThemeManager.Current.ChangeTheme(this, "Dark.Blue"); 42 | } 43 | 44 | // Show main window 45 | StartupUri = new Uri("Views/MainWindow.xaml", UriKind.Relative); 46 | } 47 | 48 | private async void Application_Exit(object sender, ExitEventArgs e) 49 | { 50 | using (AppHost) 51 | { 52 | await AppHost.StopAsync(TimeSpan.FromSeconds(5)); 53 | } 54 | } 55 | 56 | private void ConfigureServices(IServiceCollection services) 57 | { 58 | // RxUI uses Splat as its default DI engine but we can instruct it to use Microsoft DI instead 59 | services.UseMicrosoftDependencyResolver(); 60 | var resolver = Locator.CurrentMutable; 61 | resolver.InitializeSplat(); 62 | resolver.InitializeReactiveUI(); 63 | 64 | // Manual register ViewModels and Windows 65 | services.AddTransient(); 66 | services.AddTransient(); 67 | 68 | services.AddTransient(); 69 | services.AddTransient(); 70 | services.AddTransient(); 71 | 72 | // Manual register views 73 | services.AddTransient(typeof(IViewFor), typeof(Views.SelectView)); 74 | services.AddTransient(typeof(IViewFor), typeof(Views.FolderView)); 75 | services.AddTransient(typeof(IViewFor), typeof(Views.WebcamView)); 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/MachineLearning.ObjectDetect.WPF/ColorExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace MachineLearning.ObjectDetect.WPF 2 | { 3 | internal static class ColorExtensions 4 | { 5 | internal static System.Windows.Media.Color ToMediaColor(this System.Drawing.Color drawingColor) => System.Windows.Media.Color.FromArgb(drawingColor.A, drawingColor.R, drawingColor.G, drawingColor.B); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/MachineLearning.ObjectDetect.WPF/ControlStyles/ToggleSwitch.xaml: -------------------------------------------------------------------------------- 1 |  6 | 7 | 0 8 | 9 | 15 | 16 | 494 | 495 | -------------------------------------------------------------------------------- /src/MachineLearning.ObjectDetect.WPF/FodyWeavers.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | -------------------------------------------------------------------------------- /src/MachineLearning.ObjectDetect.WPF/FodyWeavers.xsd: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | Used to control if the On_PropertyName_Changed feature is enabled. 13 | 14 | 15 | 16 | 17 | Used to control if the Dependent properties feature is enabled. 18 | 19 | 20 | 21 | 22 | Used to control if the IsChanged property feature is enabled. 23 | 24 | 25 | 26 | 27 | Used to change the name of the method that fires the notify event. This is a string that accepts multiple values in a comma separated form. 28 | 29 | 30 | 31 | 32 | Used to control if equality checks should be inserted. If false, equality checking will be disabled for the project. 33 | 34 | 35 | 36 | 37 | Used to control if equality checks should use the Equals method resolved from the base class. 38 | 39 | 40 | 41 | 42 | Used to control if equality checks should use the static Equals method resolved from the base class. 43 | 44 | 45 | 46 | 47 | Used to turn off build warnings from this weaver. 48 | 49 | 50 | 51 | 52 | Used to turn off build warnings about mismatched On_PropertyName_Changed methods. 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed. 61 | 62 | 63 | 64 | 65 | A comma-separated list of error codes that can be safely ignored in assembly verification. 66 | 67 | 68 | 69 | 70 | 'false' to turn off automatic generation of the XML Schema file. 71 | 72 | 73 | 74 | 75 | -------------------------------------------------------------------------------- /src/MachineLearning.ObjectDetect.WPF/MachineLearning.ObjectDetect.WPF.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | WinExe 5 | net5.0-windows 6 | true 7 | preview 8 | enable 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | all 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | PreserveNewest 38 | 39 | 40 | PreserveNewest 41 | 42 | 43 | PreserveNewest 44 | 45 | 46 | PreserveNewest 47 | 48 | 49 | PreserveNewest 50 | 51 | 52 | PreserveNewest 53 | 54 | 55 | PreserveNewest 56 | 57 | 58 | PreserveNewest 59 | 60 | 61 | PreserveNewest 62 | 63 | 64 | PreserveNewest 65 | 66 | 67 | PreserveNewest 68 | 69 | 70 | PreserveNewest 71 | 72 | 73 | PreserveNewest 74 | 75 | 76 | PreserveNewest 77 | 78 | 79 | PreserveNewest 80 | 81 | 82 | PreserveNewest 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | -------------------------------------------------------------------------------- /src/MachineLearning.ObjectDetect.WPF/OnnxModels/TinyYolo2_model.onnx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gerardo-lijs/MachineLearning-ObjectDetect-WPF/2e48f55b95031f7abcf012ce3dee20fb2cf9918f/src/MachineLearning.ObjectDetect.WPF/OnnxModels/TinyYolo2_model.onnx -------------------------------------------------------------------------------- /src/MachineLearning.ObjectDetect.WPF/OnnxModels/facemask.ONNX.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gerardo-lijs/MachineLearning-ObjectDetect-WPF/2e48f55b95031f7abcf012ce3dee20fb2cf9918f/src/MachineLearning.ObjectDetect.WPF/OnnxModels/facemask.ONNX.zip -------------------------------------------------------------------------------- /src/MachineLearning.ObjectDetect.WPF/ReactiveMetroWindow.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Concurrent; 3 | using System.Collections.Generic; 4 | using System.Windows; 5 | 6 | using MahApps.Metro.Controls; 7 | using ReactiveUI; 8 | 9 | namespace MachineLearning.ObjectDetect.WPF 10 | { 11 | public class ReactiveMetroWindow : MetroWindow, IViewFor where TViewModel : class 12 | { 13 | public static readonly DependencyProperty ViewModelProperty = DependencyProperty.Register(nameof(ViewModel), typeof(TViewModel), typeof(ReactiveMetroWindow), new PropertyMetadata(null)); 14 | 15 | /// 16 | /// Gets the binding root view model. 17 | /// 18 | public TViewModel? BindingRoot => ViewModel; 19 | 20 | public TViewModel? ViewModel 21 | { 22 | get => (TViewModel)GetValue(ViewModelProperty); 23 | set => SetValue(ViewModelProperty, value); 24 | } 25 | 26 | object? IViewFor.ViewModel 27 | { 28 | get => ViewModel; 29 | set => ViewModel = (TViewModel?)value; 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/MachineLearning.ObjectDetect.WPF/Services/CameraOpenCv.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel; 4 | using System.Linq; 5 | using System.Reactive.Linq; 6 | using System.Reactive.Subjects; 7 | using System.Text; 8 | using System.Threading; 9 | using System.Threading.Tasks; 10 | 11 | using OpenCvSharp; 12 | 13 | namespace MachineLearning.ObjectDetect.WPF.Services 14 | { 15 | public static class CameraOpenCvEvents 16 | { 17 | public delegate void GrabContinuousStartedEventHandler(); 18 | public delegate void GrabContinuousStoppedEventHandler(); 19 | } 20 | 21 | public sealed class CameraOpenCv : IDisposable, INotifyPropertyChanged 22 | { 23 | #pragma warning disable CS0067 // Event used by Fody 24 | public event PropertyChangedEventHandler? PropertyChanged; 25 | #pragma warning restore CS0067 // Event used by Fody 26 | public record CameraDevice(int CameraIndex, string Name, string DeviceId); 27 | public record ImageGrabbedData(Mat image, double CurrentFPS); 28 | /// 29 | /// Enumerates the connected cameras. 30 | /// 31 | public static IEnumerable EnumerateAllConnectedCameras() => 32 | DirectShowLib.DsDevice.GetDevicesOfCat(DirectShowLib.FilterCategory.VideoInputDevice).Select((x, cameraIndex) => new CameraOpenCv.CameraDevice(cameraIndex++, x.Name, x.DevicePath)); 33 | 34 | private CancellationTokenSource? _cts; 35 | private readonly System.Diagnostics.Stopwatch _fpsStopWatch = new System.Diagnostics.Stopwatch(); 36 | 37 | public ISubject ImageGrabbed { get; } = new Subject(); 38 | public event CameraOpenCvEvents.GrabContinuousStartedEventHandler? GrabContinuousStarted; 39 | public event CameraOpenCvEvents.GrabContinuousStoppedEventHandler? GrabContinuousStopped; 40 | public bool IsGrabbing { get; private set; } 41 | 42 | /// 43 | /// Frame rate used to display video. 44 | /// 45 | public int MaxDisplayFrameRate { get; set; } = 30; 46 | 47 | public bool FlipImageY { get; set; } 48 | public bool FlipImageX { get; set; } 49 | public double CurrentFPS { get; private set; } 50 | 51 | public async Task GrabContinuous_Start(int cameraIndex) 52 | { 53 | _cts = new CancellationTokenSource(); 54 | await Task.Run(() => GrabContinuous_Proc(cameraIndex, _cts.Token), _cts.Token); 55 | } 56 | 57 | private async Task GrabContinuous_Proc(int cameraIndex, CancellationToken cancellationToken) 58 | { 59 | // FPS delay 60 | var fpsMilliseconds = 1000 / MaxDisplayFrameRate; 61 | 62 | // Init capture 63 | using var videoCapture = new VideoCapture(cameraIndex); 64 | videoCapture.Open(cameraIndex); 65 | if (!videoCapture.IsOpened()) throw new Exception("Could not open camera."); 66 | 67 | // TODO: Refactor this to Thread or in Caller 68 | await System.Windows.Application.Current.Dispatcher.InvokeAsync(() => 69 | { 70 | IsGrabbing = true; 71 | }); 72 | GrabContinuousStarted?.Invoke(); 73 | 74 | var fpsCounter = new List(); 75 | 76 | // Grab 77 | using var frame = new Mat(); 78 | while (!cancellationToken.IsCancellationRequested) 79 | { 80 | // Reduce the number of displayed images to a reasonable amount if the camera is acquiring images very fast. 81 | if (!_fpsStopWatch.IsRunning || _fpsStopWatch.ElapsedMilliseconds > fpsMilliseconds) 82 | { 83 | // Display FPS counter 84 | if (_fpsStopWatch.IsRunning) 85 | { 86 | fpsCounter.Add(1000 / _fpsStopWatch.ElapsedMilliseconds); 87 | if (fpsCounter.Count > MaxDisplayFrameRate / 2) 88 | { 89 | CurrentFPS = fpsCounter.Average(); 90 | fpsCounter.Clear(); 91 | } 92 | } 93 | 94 | _fpsStopWatch.Restart(); 95 | 96 | // Get frame 97 | videoCapture.Read(frame); 98 | 99 | if (!frame.Empty()) 100 | { 101 | // Optional flip 102 | Mat workFrame = FlipImageY ? frame.Flip(FlipMode.Y) : frame; 103 | workFrame = FlipImageX ? workFrame.Flip(FlipMode.X) : workFrame; 104 | 105 | if (!cancellationToken.IsCancellationRequested) 106 | { 107 | ImageGrabbed.OnNext(new ImageGrabbedData(workFrame, CurrentFPS)); 108 | } 109 | } 110 | } 111 | else 112 | { 113 | // Display frame rate speed to get desired display frame rate. We use half the expected time to consider time spent executing other code 114 | var fpsDelay = (fpsMilliseconds / 2) - (int)_fpsStopWatch.ElapsedMilliseconds; 115 | if (fpsDelay > 0) await Task.Delay(fpsDelay, CancellationToken.None); 116 | } 117 | } 118 | 119 | videoCapture.Release(); 120 | // TODO: Refactor this to Thread or in Caller 121 | await System.Windows.Application.Current.Dispatcher.InvokeAsync(() => 122 | { 123 | IsGrabbing = false; 124 | CurrentFPS = 0; 125 | }); 126 | GrabContinuousStopped?.Invoke(); 127 | } 128 | 129 | public async Task GrabContinuous_Stop() 130 | { 131 | // Check 132 | if (_cts is null) return; 133 | 134 | // If "Dispose" gets called before Stop 135 | if (_cts.IsCancellationRequested) return; 136 | 137 | _cts.Cancel(); 138 | 139 | // Wait for grab to finish - TODO: Refactor this! 140 | await Task.Delay(250); 141 | } 142 | 143 | public void Dispose() 144 | { 145 | _cts?.Cancel(); 146 | } 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /src/MachineLearning.ObjectDetect.WPF/TestImages/00_maksssksksss598.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gerardo-lijs/MachineLearning-ObjectDetect-WPF/2e48f55b95031f7abcf012ce3dee20fb2cf9918f/src/MachineLearning.ObjectDetect.WPF/TestImages/00_maksssksksss598.png -------------------------------------------------------------------------------- /src/MachineLearning.ObjectDetect.WPF/TestImages/01_maksssksksss348.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gerardo-lijs/MachineLearning-ObjectDetect-WPF/2e48f55b95031f7abcf012ce3dee20fb2cf9918f/src/MachineLearning.ObjectDetect.WPF/TestImages/01_maksssksksss348.png -------------------------------------------------------------------------------- /src/MachineLearning.ObjectDetect.WPF/TestImages/02_maksssksksss531.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gerardo-lijs/MachineLearning-ObjectDetect-WPF/2e48f55b95031f7abcf012ce3dee20fb2cf9918f/src/MachineLearning.ObjectDetect.WPF/TestImages/02_maksssksksss531.png -------------------------------------------------------------------------------- /src/MachineLearning.ObjectDetect.WPF/TestImages/maksssksksss432.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gerardo-lijs/MachineLearning-ObjectDetect-WPF/2e48f55b95031f7abcf012ce3dee20fb2cf9918f/src/MachineLearning.ObjectDetect.WPF/TestImages/maksssksksss432.png -------------------------------------------------------------------------------- /src/MachineLearning.ObjectDetect.WPF/TestImages/maksssksksss490.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gerardo-lijs/MachineLearning-ObjectDetect-WPF/2e48f55b95031f7abcf012ce3dee20fb2cf9918f/src/MachineLearning.ObjectDetect.WPF/TestImages/maksssksksss490.png -------------------------------------------------------------------------------- /src/MachineLearning.ObjectDetect.WPF/TestImages/maksssksksss504.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gerardo-lijs/MachineLearning-ObjectDetect-WPF/2e48f55b95031f7abcf012ce3dee20fb2cf9918f/src/MachineLearning.ObjectDetect.WPF/TestImages/maksssksksss504.png -------------------------------------------------------------------------------- /src/MachineLearning.ObjectDetect.WPF/TestImages/maksssksksss528.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gerardo-lijs/MachineLearning-ObjectDetect-WPF/2e48f55b95031f7abcf012ce3dee20fb2cf9918f/src/MachineLearning.ObjectDetect.WPF/TestImages/maksssksksss528.png -------------------------------------------------------------------------------- /src/MachineLearning.ObjectDetect.WPF/TestImages/maksssksksss530.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gerardo-lijs/MachineLearning-ObjectDetect-WPF/2e48f55b95031f7abcf012ce3dee20fb2cf9918f/src/MachineLearning.ObjectDetect.WPF/TestImages/maksssksksss530.png -------------------------------------------------------------------------------- /src/MachineLearning.ObjectDetect.WPF/TestImages/maksssksksss532.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gerardo-lijs/MachineLearning-ObjectDetect-WPF/2e48f55b95031f7abcf012ce3dee20fb2cf9918f/src/MachineLearning.ObjectDetect.WPF/TestImages/maksssksksss532.png -------------------------------------------------------------------------------- /src/MachineLearning.ObjectDetect.WPF/TestImages/maksssksksss537.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gerardo-lijs/MachineLearning-ObjectDetect-WPF/2e48f55b95031f7abcf012ce3dee20fb2cf9918f/src/MachineLearning.ObjectDetect.WPF/TestImages/maksssksksss537.png -------------------------------------------------------------------------------- /src/MachineLearning.ObjectDetect.WPF/TestImages/maksssksksss561.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gerardo-lijs/MachineLearning-ObjectDetect-WPF/2e48f55b95031f7abcf012ce3dee20fb2cf9918f/src/MachineLearning.ObjectDetect.WPF/TestImages/maksssksksss561.png -------------------------------------------------------------------------------- /src/MachineLearning.ObjectDetect.WPF/TestImages/maksssksksss565.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gerardo-lijs/MachineLearning-ObjectDetect-WPF/2e48f55b95031f7abcf012ce3dee20fb2cf9918f/src/MachineLearning.ObjectDetect.WPF/TestImages/maksssksksss565.png -------------------------------------------------------------------------------- /src/MachineLearning.ObjectDetect.WPF/TestImages/maksssksksss571.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gerardo-lijs/MachineLearning-ObjectDetect-WPF/2e48f55b95031f7abcf012ce3dee20fb2cf9918f/src/MachineLearning.ObjectDetect.WPF/TestImages/maksssksksss571.png -------------------------------------------------------------------------------- /src/MachineLearning.ObjectDetect.WPF/ViewModels/FolderViewModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Reactive; 5 | using System.Reactive.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | using System.Drawing; 10 | using System.Windows.Media.Imaging; 11 | 12 | using ReactiveUI; 13 | using ReactiveUI.Fody.Helpers; 14 | using Splat; 15 | 16 | using OnnxObjectDetection; 17 | 18 | namespace MachineLearning.ObjectDetect.WPF.ViewModels 19 | { 20 | public class FolderViewModel : ReactiveObject, IRoutableViewModel 21 | { 22 | public string UrlPathSegment => "FolderView"; 23 | public IScreen HostScreen { get; } 24 | private readonly MainWindowViewModel _mainViewModel; 25 | 26 | // Commands 27 | public ReactiveCommand NavigateBack { get; } 28 | public ReactiveCommand PrevImage { get; } 29 | public ReactiveCommand NextImage { get; } 30 | public ReactiveCommand SelectImageFolder { get; } 31 | 32 | [Reactive] public string ImageFolderPath { get; private set; } = string.Empty; 33 | [Reactive] public List ImageList { get; private set; } = new List(); 34 | [Reactive] public int ImageCurrentIndex { get; private set; } 35 | 36 | [Reactive] public long DetectMilliseconds { get; private set; } 37 | [Reactive] public BitmapSource? DetectImageSource { get; private set; } 38 | public List FilteredBoundingBoxes { get; private set; } = new List(); 39 | 40 | // Interactions 41 | public readonly Interaction DrawOverlays = new Interaction(); 42 | 43 | public FolderViewModel(IScreen? screen = null) 44 | { 45 | HostScreen = screen ?? Locator.Current.GetService(); 46 | _mainViewModel = HostScreen as MainWindowViewModel ?? throw new Exception("IScreen must be of type MainWindowViewModel"); 47 | 48 | // Create command 49 | NavigateBack = ReactiveCommand.CreateFromTask(NavigateBackImpl); 50 | PrevImage = ReactiveCommand.CreateFromTask(PrevImageImpl); 51 | NextImage = ReactiveCommand.CreateFromTask(NextImageImpl); 52 | SelectImageFolder = ReactiveCommand.CreateFromTask(SelectImageFolderImpl); 53 | 54 | // Observables 55 | this.WhenAnyValue(x => x.ImageFolderPath) 56 | .Skip(1) 57 | .Subscribe(folder => 58 | { 59 | if (string.IsNullOrWhiteSpace(folder)) return; 60 | ImageList = System.IO.Directory.GetFiles(folder).Where(x => x.ToLowerInvariant().EndsWith(".png") || x.ToLowerInvariant().EndsWith(".jpg") || x.ToLowerInvariant().EndsWith(".jpeg") || x.ToLowerInvariant().EndsWith(".bmp")).ToList(); 61 | }); 62 | 63 | // Load image list 64 | ImageFolderPath = System.IO.Path.Combine(Environment.CurrentDirectory, "TestImages"); 65 | } 66 | 67 | private async Task NavigateBackImpl() 68 | { 69 | await _mainViewModel.Router.NavigateBack.Execute(); 70 | } 71 | 72 | private async Task SelectImageFolderImpl() 73 | { 74 | using var dialog = new System.Windows.Forms.FolderBrowserDialog 75 | { 76 | ShowNewFolderButton = false, 77 | SelectedPath = ImageFolderPath 78 | }; 79 | 80 | if (dialog.ShowDialog() == System.Windows.Forms.DialogResult.OK) 81 | { 82 | ImageFolderPath = dialog.SelectedPath; 83 | ImageCurrentIndex = 0; 84 | if (ImageList.Count > 0) 85 | { 86 | await NextImage.Execute(); 87 | } 88 | } 89 | } 90 | 91 | private async Task PrevImageImpl() 92 | { 93 | if (ImageCurrentIndex <= 1) return; 94 | ImageCurrentIndex--; 95 | await LoadAndDetectImage(ImageList[ImageCurrentIndex - 1]); 96 | } 97 | 98 | private async Task NextImageImpl() 99 | { 100 | if (ImageList.Count == ImageCurrentIndex) return; 101 | ImageCurrentIndex++; 102 | await LoadAndDetectImage(ImageList[ImageCurrentIndex - 1]); 103 | } 104 | 105 | private async Task DetectImage(Bitmap bitmap) 106 | { 107 | var imageInputData = new ImageInputData { Image = bitmap }; 108 | 109 | var sw = System.Diagnostics.Stopwatch.StartNew(); 110 | 111 | FilteredBoundingBoxes = _mainViewModel.DetectObjects(imageInputData); 112 | 113 | // Time spent for detection by ML.NET 114 | DetectMilliseconds = sw.ElapsedMilliseconds; 115 | 116 | await DrawOverlays.Handle(Unit.Default); 117 | } 118 | 119 | private async Task LoadAndDetectImage(string filename) 120 | { 121 | var bitmapImage = new Bitmap(filename); 122 | DetectImageSource = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(bitmapImage.GetHbitmap(), IntPtr.Zero, System.Windows.Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions()); 123 | await DetectImage(bitmapImage); 124 | } 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /src/MachineLearning.ObjectDetect.WPF/ViewModels/MainWindowViewModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Reactive; 5 | using System.Reactive.Linq; 6 | using System.Reactive.Disposables; 7 | using System.Threading.Tasks; 8 | 9 | using Splat; 10 | using ReactiveUI; 11 | using ReactiveUI.Fody.Helpers; 12 | using ControlzEx.Theming; 13 | 14 | using Microsoft.ML; 15 | using OnnxObjectDetection; 16 | 17 | namespace MachineLearning.ObjectDetect.WPF.ViewModels 18 | { 19 | public class MainWindowViewModel : ReactiveObject, IScreen 20 | { 21 | public RoutingState Router { get; } 22 | 23 | public string ModelsDirectory { get; } 24 | 25 | private OnnxOutputParser? OutputParser; 26 | private PredictionEngine? CustomVisionPredictionEngine; 27 | private PredictionEngine? TinyYoloPredictionEngine; 28 | 29 | [Reactive] public bool IsLightTheme { get; set; } 30 | 31 | public MainWindowViewModel() 32 | { 33 | Locator.CurrentMutable.RegisterConstant(this, typeof(IScreen)); 34 | 35 | ModelsDirectory = System.IO.Path.Combine(Environment.CurrentDirectory, @"OnnxModels"); 36 | 37 | // Initialize the Router. 38 | Router = new RoutingState(); 39 | 40 | // Current theme 41 | var theme = ThemeManager.Current.DetectTheme(); 42 | IsLightTheme = theme is null || theme.BaseColorScheme == ThemeManager.BaseColorLight; 43 | 44 | // Theme change 45 | this.WhenAnyValue(x => x.IsLightTheme) 46 | .Skip(1) 47 | .Subscribe(isLightTheme => 48 | { 49 | if (isLightTheme) 50 | ThemeManager.Current.ChangeThemeBaseColor(System.Windows.Application.Current, "Light"); 51 | else 52 | ThemeManager.Current.ChangeThemeBaseColor(System.Windows.Application.Current, "Dark"); 53 | }); 54 | } 55 | 56 | public void LoadModel(string modelFilename) 57 | { 58 | var modelFullFilename = System.IO.Path.Combine(ModelsDirectory, modelFilename); 59 | 60 | // Load Onnx model 61 | if (modelFilename.EndsWith(".zip")) 62 | { 63 | var customVisionModel = new CustomVisionModel(modelFullFilename); 64 | var modelConfigurator = new OnnxModelConfigurator(customVisionModel); 65 | 66 | OutputParser = new OnnxOutputParser(customVisionModel); 67 | CustomVisionPredictionEngine = modelConfigurator.GetMlNetPredictionEngine(); 68 | TinyYoloPredictionEngine = null; 69 | } 70 | else 71 | { 72 | var tinyYoloModel = new TinyYoloModel(modelFullFilename); 73 | var modelConfigurator = new OnnxModelConfigurator(tinyYoloModel); 74 | 75 | OutputParser = new OnnxOutputParser(tinyYoloModel); 76 | TinyYoloPredictionEngine = modelConfigurator.GetMlNetPredictionEngine(); 77 | CustomVisionPredictionEngine = null; 78 | } 79 | } 80 | 81 | public List DetectObjects(ImageInputData imageInputData) 82 | { 83 | if (OutputParser is null) throw new Exception("Model not loaded."); 84 | 85 | var labels = CustomVisionPredictionEngine?.Predict(imageInputData).PredictedLabels ?? TinyYoloPredictionEngine?.Predict(imageInputData).PredictedLabels; 86 | var boundingBoxes = OutputParser.ParseOutputs(labels); 87 | var filteredBoxes = OutputParser.FilterBoundingBoxes(boundingBoxes, 5, 0.5f); 88 | return filteredBoxes; 89 | } 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/MachineLearning.ObjectDetect.WPF/ViewModels/SelectViewModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Reactive; 5 | using System.Reactive.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | using System.Drawing; 10 | using System.Windows.Media.Imaging; 11 | 12 | using ReactiveUI; 13 | using ReactiveUI.Fody.Helpers; 14 | using Splat; 15 | 16 | using OnnxObjectDetection; 17 | 18 | namespace MachineLearning.ObjectDetect.WPF.ViewModels 19 | { 20 | public class SelectViewModel : ReactiveObject, IRoutableViewModel 21 | { 22 | public string UrlPathSegment => "SelectView"; 23 | public IScreen HostScreen { get; } 24 | private readonly MainWindowViewModel _mainViewModel; 25 | 26 | public List Models { get; } 27 | [Reactive] public string? ModelSelected { get; set; } 28 | 29 | // Commands 30 | public ReactiveCommand FolderViewSelect { get; } 31 | public ReactiveCommand WebcamViewSelect { get; } 32 | 33 | public SelectViewModel(IScreen? screen = null) 34 | { 35 | HostScreen = screen ?? Locator.Current.GetService(); 36 | _mainViewModel = HostScreen as MainWindowViewModel ?? throw new Exception("IScreen must be of type MainWindowViewModel"); 37 | 38 | // Enumerate models 39 | Models = System.IO.Directory.GetFiles(_mainViewModel.ModelsDirectory).Where(x => x.EndsWith(".zip") || x.EndsWith(".onnx")).Select(x => System.IO.Path.GetFileName(x)).ToList(); 40 | ModelSelected = Models.FirstOrDefault(); 41 | 42 | // Observables 43 | this.WhenAnyValue(x => x.ModelSelected) 44 | .WhereNotNull() 45 | .Subscribe(modelFilename => 46 | { 47 | _mainViewModel.LoadModel(modelFilename); 48 | }); 49 | 50 | // Create command 51 | FolderViewSelect = ReactiveCommand.CreateFromObservable(FolderViewSelectImpl); 52 | WebcamViewSelect = ReactiveCommand.CreateFromObservable(WebcamViewSelectImpl); 53 | } 54 | 55 | private IObservable FolderViewSelectImpl() 56 | { 57 | _mainViewModel.Router.Navigate.Execute(Locator.Current.GetService()).Subscribe(); 58 | return Observable.Return(Unit.Default); 59 | } 60 | 61 | private IObservable WebcamViewSelectImpl() 62 | { 63 | _mainViewModel.Router.Navigate.Execute(Locator.Current.GetService()).Subscribe(); 64 | return Observable.Return(Unit.Default); 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/MachineLearning.ObjectDetect.WPF/ViewModels/WebcamViewModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Reactive; 5 | using System.Reactive.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | using System.Reactive.Disposables; 9 | 10 | using System.Drawing; 11 | 12 | using ReactiveUI; 13 | using ReactiveUI.Fody.Helpers; 14 | using Splat; 15 | 16 | using OpenCvSharp.Extensions; 17 | using OnnxObjectDetection; 18 | 19 | namespace MachineLearning.ObjectDetect.WPF.ViewModels 20 | { 21 | public class WebcamViewModel : ReactiveObject, IActivatableViewModel, IRoutableViewModel 22 | { 23 | public string UrlPathSegment => "WebcamView"; 24 | public IScreen HostScreen { get; } 25 | public ViewModelActivator Activator { get; } = new ViewModelActivator(); 26 | private readonly CompositeDisposable _cleanUp = new CompositeDisposable(); 27 | private readonly MainWindowViewModel _mainViewModel; 28 | 29 | public Services.CameraOpenCv CameraOpenCv { get; } 30 | 31 | public List CameraDevices { get; } 32 | [Reactive] public Services.CameraOpenCv.CameraDevice? CameraDeviceSelected { get; set; } 33 | 34 | public List FilteredBoundingBoxes { get; private set; } = new List(); 35 | 36 | // Commands 37 | public ReactiveCommand NavigateBack { get; } 38 | public ReactiveCommand GrabContinuous_Start { get; } 39 | public ReactiveCommand GrabContinuous_Stop { get; } 40 | 41 | // Interactions 42 | public readonly Interaction DrawOverlays = new Interaction(); 43 | 44 | [Reactive] public bool DetectObjects { get; set; } 45 | 46 | public WebcamViewModel(IScreen? screen = null) 47 | { 48 | HostScreen = screen ?? Locator.Current.GetService(); 49 | _mainViewModel = HostScreen as MainWindowViewModel ?? throw new Exception("IScreen must be of type MainWindowViewModel"); 50 | 51 | // Create command 52 | NavigateBack = ReactiveCommand.CreateFromTask(NavigateBackImpl); 53 | GrabContinuous_Start = ReactiveCommand.CreateFromTask(GrabContinuous_StartImpl); 54 | GrabContinuous_Stop = ReactiveCommand.CreateFromTask(GrabContinuous_StopImpl); 55 | 56 | // Enumerate cameras 57 | CameraDevices = Services.CameraOpenCv.EnumerateAllConnectedCameras().ToList(); 58 | CameraDeviceSelected = CameraDevices.FirstOrDefault(); 59 | 60 | // For this sample we init the camera here. We could also have a Singleton camera and get it directly with DI 61 | CameraOpenCv = new Services.CameraOpenCv(); 62 | //CameraOpenCv.GrabContinuousStarted += CameraOpenCv_GrabContinuousStarted; 63 | //CameraOpenCv.GrabContinuousStopped += CameraOpenCv_GrabContinuousStopped; 64 | CameraOpenCv.ImageGrabbed.Subscribe(async imageGrabbedData => 65 | { 66 | // Check 67 | if (!DetectObjects) return; 68 | 69 | // Detect 70 | var imageInputData = new ImageInputData { Image = imageGrabbedData.image.ToBitmap() }; 71 | FilteredBoundingBoxes = _mainViewModel.DetectObjects(imageInputData); 72 | await DrawOverlays.Handle(Unit.Default); 73 | }) 74 | .DisposeWith(_cleanUp); 75 | 76 | this.WhenActivated(disposables => 77 | { 78 | Disposable 79 | .Create(() => HandleDeactivation()) 80 | .DisposeWith(disposables); 81 | }); 82 | } 83 | 84 | private void HandleDeactivation() 85 | { 86 | _cleanUp.Dispose(); 87 | CameraOpenCv?.Dispose(); 88 | } 89 | 90 | private async Task NavigateBackImpl() 91 | { 92 | await GrabContinuous_Stop.Execute(); 93 | await _mainViewModel.Router.NavigateBack.Execute(); 94 | } 95 | 96 | private async Task GrabContinuous_StartImpl() 97 | { 98 | // Check 99 | if (CameraDeviceSelected is null) throw new Exception("Camera device not selected."); 100 | 101 | // Grab 102 | await CameraOpenCv.GrabContinuous_Start(CameraDeviceSelected.CameraIndex); 103 | } 104 | 105 | private async Task GrabContinuous_StopImpl() 106 | { 107 | // Cancel and dispose 108 | await CameraOpenCv.GrabContinuous_Stop(); 109 | } 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /src/MachineLearning.ObjectDetect.WPF/Views/FolderView.xaml: -------------------------------------------------------------------------------- 1 |  14 | 15 | 16 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 |