├── .gitattributes ├── .gitignore ├── BlazorML5.sln ├── BlazorML5 ├── BlazorML5.csproj ├── Helpers │ ├── FeatureExtractor.cs │ ├── KnnClassifier.cs │ ├── NeuralNetwork.cs │ ├── NeuralNetworkOptions.cs │ └── UtilHelper.cs ├── Image │ ├── FaceMesh.cs │ ├── FaceMeshOptions.cs │ ├── ImageClassifier.cs │ ├── ObjectDetector.cs │ ├── PoseNet.cs │ └── PoseNetOptions.cs ├── Ml5.cs ├── Sound │ └── SoundClassifier.cs ├── Text │ └── Sentiment.cs ├── _Imports.razor └── _imports.razor.cs ├── LICENSE ├── README.md └── SampleApp ├── App.razor ├── Pages ├── FaceMeshPage.razor ├── FeatureExtractorPage.razor ├── ImageClassifierPage.razor ├── Index.razor ├── KnnClassifier.razor ├── NeuralNetPage.razor ├── NnPage.razor ├── ObjectDetectorPage.razor ├── PosenetPage.razor ├── SentimentPage.razor └── SoundClassifer.razor ├── Program.cs ├── Properties └── launchSettings.json ├── SampleApp.csproj ├── SampleApp.sln ├── Shared ├── MainLayout.razor ├── MainLayout.razor.css ├── NavMenu.razor └── NavMenu.razor.css ├── _Imports.razor └── wwwroot ├── css ├── app.css └── open-iconic │ ├── FONT-LICENSE │ ├── ICON-LICENSE │ ├── README.md │ └── font │ ├── css │ └── open-iconic-bootstrap.min.css │ └── fonts │ ├── open-iconic.eot │ ├── open-iconic.otf │ ├── open-iconic.svg │ ├── open-iconic.ttf │ └── open-iconic.woff ├── data ├── dog.jpg ├── pose.jpg └── titanic.json ├── favicon.ico ├── icon-192.png └── index.html /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.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 | .idea/ 14 | .vs/ 15 | .vscode/ 16 | 17 | # User-specific files (MonoDevelop/Xamarin Studio) 18 | *.userprefs 19 | 20 | # Mono auto generated files 21 | mono_crash.* 22 | 23 | # Build results 24 | [Dd]ebug/ 25 | [Dd]ebugPublic/ 26 | [Rr]elease/ 27 | [Rr]eleases/ 28 | x64/ 29 | x86/ 30 | [Aa][Rr][Mm]/ 31 | [Aa][Rr][Mm]64/ 32 | bld/ 33 | [Bb]in/ 34 | [Oo]bj/ 35 | [Ll]og/ 36 | 37 | # Visual Studio 2015/2017 cache/options directory 38 | .vs/ 39 | # Uncomment if you have tasks that create the project's static files in wwwroot 40 | #wwwroot/ 41 | 42 | # Visual Studio 2017 auto generated files 43 | Generated\ Files/ 44 | 45 | # MSTest test Results 46 | [Tt]est[Rr]esult*/ 47 | [Bb]uild[Ll]og.* 48 | 49 | # NUNIT 50 | *.VisualState.xml 51 | TestResult.xml 52 | 53 | # Build Results of an ATL Project 54 | [Dd]ebugPS/ 55 | [Rr]eleasePS/ 56 | dlldata.c 57 | 58 | # Benchmark Results 59 | BenchmarkDotNet.Artifacts/ 60 | 61 | # .NET Core 62 | project.lock.json 63 | project.fragment.lock.json 64 | artifacts/ 65 | 66 | # StyleCop 67 | StyleCopReport.xml 68 | 69 | # Files built by Visual Studio 70 | *_i.c 71 | *_p.c 72 | *_h.h 73 | *.ilk 74 | *.meta 75 | *.obj 76 | *.iobj 77 | *.pch 78 | *.pdb 79 | *.ipdb 80 | *.pgc 81 | *.pgd 82 | *.rsp 83 | *.sbr 84 | *.tlb 85 | *.tli 86 | *.tlh 87 | *.tmp 88 | *.tmp_proj 89 | *_wpftmp.csproj 90 | *.log 91 | *.vspscc 92 | *.vssscc 93 | .builds 94 | *.pidb 95 | *.svclog 96 | *.scc 97 | 98 | # Chutzpah Test files 99 | _Chutzpah* 100 | 101 | # Visual C++ cache files 102 | ipch/ 103 | *.aps 104 | *.ncb 105 | *.opendb 106 | *.opensdf 107 | *.sdf 108 | *.cachefile 109 | *.VC.db 110 | *.VC.VC.opendb 111 | 112 | # Visual Studio profiler 113 | *.psess 114 | *.vsp 115 | *.vspx 116 | *.sap 117 | 118 | # Visual Studio Trace Files 119 | *.e2e 120 | 121 | # TFS 2012 Local Workspace 122 | $tf/ 123 | 124 | # Guidance Automation Toolkit 125 | *.gpState 126 | 127 | # ReSharper is a .NET coding add-in 128 | _ReSharper*/ 129 | *.[Rr]e[Ss]harper 130 | *.DotSettings.user 131 | 132 | # JustCode is a .NET coding add-in 133 | .JustCode 134 | 135 | # TeamCity is a build add-in 136 | _TeamCity* 137 | 138 | # DotCover is a Code Coverage Tool 139 | *.dotCover 140 | 141 | # AxoCover is a Code Coverage Tool 142 | .axoCover/* 143 | !.axoCover/settings.json 144 | 145 | # Visual Studio code coverage results 146 | *.coverage 147 | *.coveragexml 148 | 149 | # NCrunch 150 | _NCrunch_* 151 | .*crunch*.local.xml 152 | nCrunchTemp_* 153 | 154 | # MightyMoose 155 | *.mm.* 156 | AutoTest.Net/ 157 | 158 | # Web workbench (sass) 159 | .sass-cache/ 160 | 161 | # Installshield output folder 162 | [Ee]xpress/ 163 | 164 | # DocProject is a documentation generator add-in 165 | DocProject/buildhelp/ 166 | DocProject/Help/*.HxT 167 | DocProject/Help/*.HxC 168 | DocProject/Help/*.hhc 169 | DocProject/Help/*.hhk 170 | DocProject/Help/*.hhp 171 | DocProject/Help/Html2 172 | DocProject/Help/html 173 | 174 | # Click-Once directory 175 | publish/ 176 | 177 | # Publish Web Output 178 | *.[Pp]ublish.xml 179 | *.azurePubxml 180 | # Note: Comment the next line if you want to checkin your web deploy settings, 181 | # but database connection strings (with potential passwords) will be unencrypted 182 | *.pubxml 183 | *.publishproj 184 | 185 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 186 | # checkin your Azure Web App publish settings, but sensitive information contained 187 | # in these scripts will be unencrypted 188 | PublishScripts/ 189 | 190 | # NuGet Packages 191 | *.nupkg 192 | # The packages folder can be ignored because of Package Restore 193 | **/[Pp]ackages/* 194 | # except build/, which is used as an MSBuild target. 195 | !**/[Pp]ackages/build/ 196 | # Uncomment if necessary however generally it will be regenerated when needed 197 | #!**/[Pp]ackages/repositories.config 198 | # NuGet v3's project.json files produces more ignorable files 199 | *.nuget.props 200 | *.nuget.targets 201 | 202 | # Microsoft Azure Build Output 203 | csx/ 204 | *.build.csdef 205 | 206 | # Microsoft Azure Emulator 207 | ecf/ 208 | rcf/ 209 | 210 | # Windows Store app package directories and files 211 | AppPackages/ 212 | BundleArtifacts/ 213 | Package.StoreAssociation.xml 214 | _pkginfo.txt 215 | *.appx 216 | *.appxbundle 217 | *.appxupload 218 | 219 | # Visual Studio cache files 220 | # files ending in .cache can be ignored 221 | *.[Cc]ache 222 | # but keep track of directories ending in .cache 223 | !?*.[Cc]ache/ 224 | 225 | # Others 226 | ClientBin/ 227 | ~$* 228 | *~ 229 | *.dbmdl 230 | *.dbproj.schemaview 231 | *.jfm 232 | *.pfx 233 | *.publishsettings 234 | orleans.codegen.cs 235 | 236 | # Including strong name files can present a security risk 237 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 238 | #*.snk 239 | 240 | # Since there are multiple workflows, uncomment next line to ignore bower_components 241 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 242 | #bower_components/ 243 | 244 | # RIA/Silverlight projects 245 | Generated_Code/ 246 | 247 | # Backup & report files from converting an old project file 248 | # to a newer Visual Studio version. Backup files are not needed, 249 | # because we have git ;-) 250 | _UpgradeReport_Files/ 251 | Backup*/ 252 | UpgradeLog*.XML 253 | UpgradeLog*.htm 254 | ServiceFabricBackup/ 255 | *.rptproj.bak 256 | 257 | # SQL Server files 258 | *.mdf 259 | *.ldf 260 | *.ndf 261 | 262 | # Business Intelligence projects 263 | *.rdl.data 264 | *.bim.layout 265 | *.bim_*.settings 266 | *.rptproj.rsuser 267 | *- Backup*.rdl 268 | 269 | # Microsoft Fakes 270 | FakesAssemblies/ 271 | 272 | # GhostDoc plugin setting file 273 | *.GhostDoc.xml 274 | 275 | # Node.js Tools for Visual Studio 276 | .ntvs_analysis.dat 277 | node_modules/ 278 | 279 | # Visual Studio 6 build log 280 | *.plg 281 | 282 | # Visual Studio 6 workspace options file 283 | *.opt 284 | 285 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 286 | *.vbw 287 | 288 | # Visual Studio LightSwitch build output 289 | **/*.HTMLClient/GeneratedArtifacts 290 | **/*.DesktopClient/GeneratedArtifacts 291 | **/*.DesktopClient/ModelManifest.xml 292 | **/*.Server/GeneratedArtifacts 293 | **/*.Server/ModelManifest.xml 294 | _Pvt_Extensions 295 | 296 | # Paket dependency manager 297 | .paket/paket.exe 298 | paket-files/ 299 | 300 | # FAKE - F# Make 301 | .fake/ 302 | 303 | # CodeRush personal settings 304 | .cr/personal 305 | 306 | # Python Tools for Visual Studio (PTVS) 307 | __pycache__/ 308 | *.pyc 309 | 310 | # Cake - Uncomment if you are using it 311 | # tools/** 312 | # !tools/packages.config 313 | 314 | # Tabs Studio 315 | *.tss 316 | 317 | # Telerik's JustMock configuration file 318 | *.jmconfig 319 | 320 | # BizTalk build output 321 | *.btp.cs 322 | *.btm.cs 323 | *.odx.cs 324 | *.xsd.cs 325 | 326 | # OpenCover UI analysis results 327 | OpenCover/ 328 | 329 | # Azure Stream Analytics local run output 330 | ASALocalRun/ 331 | 332 | # MSBuild Binary and Structured Log 333 | *.binlog 334 | 335 | # NVidia Nsight GPU debugger configuration file 336 | *.nvuser 337 | 338 | # MFractors (Xamarin productivity tool) working folder 339 | .mfractor/ 340 | 341 | # Local History for Visual Studio 342 | .localhistory/ 343 | 344 | # BeatPulse healthcheck temp database 345 | healthchecksdb 346 | 347 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 348 | MigrationBackup/ -------------------------------------------------------------------------------- /BlazorML5.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.1.32113.165 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SampleApp", "SampleApp\SampleApp.csproj", "{161C8D67-5D3A-4D2C-BB4C-8ED49B811650}" 7 | EndProject 8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BlazorML5", "BlazorML5\BlazorML5.csproj", "{3D798BAC-0E25-4663-88E4-9F2EA5303FA6}" 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 | {161C8D67-5D3A-4D2C-BB4C-8ED49B811650}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 17 | {161C8D67-5D3A-4D2C-BB4C-8ED49B811650}.Debug|Any CPU.Build.0 = Debug|Any CPU 18 | {161C8D67-5D3A-4D2C-BB4C-8ED49B811650}.Release|Any CPU.ActiveCfg = Release|Any CPU 19 | {161C8D67-5D3A-4D2C-BB4C-8ED49B811650}.Release|Any CPU.Build.0 = Release|Any CPU 20 | {3D798BAC-0E25-4663-88E4-9F2EA5303FA6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 21 | {3D798BAC-0E25-4663-88E4-9F2EA5303FA6}.Debug|Any CPU.Build.0 = Debug|Any CPU 22 | {3D798BAC-0E25-4663-88E4-9F2EA5303FA6}.Release|Any CPU.ActiveCfg = Release|Any CPU 23 | {3D798BAC-0E25-4663-88E4-9F2EA5303FA6}.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 = {A407A784-2C15-4B76-97C0-EB572B63EDAF} 30 | EndGlobalSection 31 | EndGlobal 32 | -------------------------------------------------------------------------------- /BlazorML5/BlazorML5.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net6.0 5 | enable 6 | enable 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /BlazorML5/Helpers/FeatureExtractor.cs: -------------------------------------------------------------------------------- 1 | using BlazorBindGen; 2 | 3 | namespace BlazorML5.Helpers; 4 | 5 | public class FeatureExtractor 6 | { 7 | #nullable disable 8 | private JObjPtr _featureExtractor; 9 | 10 | public ValueTask ModelLoaded => _featureExtractor.PropValAsync("modelLoaded"); 11 | public ValueTask HasAnyTrainedClass => _featureExtractor.PropValAsync("hasAnyTrainedClass"); 12 | public ValueTask UsageType=> _featureExtractor.PropValAsync("usageType"); 13 | public ValueTask IsPredicting => _featureExtractor.PropValAsync("isPredicting"); 14 | 15 | internal FeatureExtractor(){} 16 | #nullable restore 17 | internal FeatureExtractor Init(JObjPtr ptr) 18 | { 19 | _featureExtractor = ptr; 20 | return this; 21 | } 22 | /// 23 | /// Get a new classification based feature extractor 24 | /// 25 | /// optional video element or ElementReference 26 | /// 27 | public async Task ClassificationAsync(object? video = default) 28 | { 29 | JObjPtr nPtr; 30 | var n = new FeatureExtractor(); 31 | if (video is not null) 32 | nPtr=await _featureExtractor.CallRefAsync("classification", video,(JSCallback)n.OnVideoLoadedCallback); 33 | else 34 | nPtr=await _featureExtractor.CallRefAsync("classification",(JSCallback)n.OnVideoLoadedCallback); 35 | return n.Init(nPtr); 36 | } 37 | /// 38 | /// Get a new regression based feature extractor 39 | /// 40 | /// optional video element or ElementReference 41 | /// 42 | public async Task RegressionAsync(object? video = default) 43 | { 44 | JObjPtr nPtr; 45 | var n = new FeatureExtractor(); 46 | if (video is not null) 47 | nPtr=await _featureExtractor.CallRefAsync("regression", video,(JSCallback)n.OnVideoLoadedCallback); 48 | else 49 | nPtr=await _featureExtractor.CallRefAsync("regression",(JSCallback)n.OnVideoLoadedCallback); 50 | return n.Init(nPtr); 51 | } 52 | 53 | /// 54 | /// Adds a new image element to the featureExtractor for training 55 | /// 56 | /// The label to associate the new image with. When using the classifier this can be strings or numbers. For a regression, this needs to be a number. 57 | /// Optional. An HTML image or video element or a p5 image or video element. If not input is provided, the video element provided in the method-type will be used. 58 | /// 59 | public async Task AddImageAsync(T label, object? input = default) 60 | { 61 | if(input is null) 62 | await _featureExtractor.CallRefAsync("addImage", label!,(JSCallback)OnImageAddedCallback); 63 | else 64 | await _featureExtractor.CallVoidAsync("addImage", label!, input,(JSCallback)OnImageAddedCallback); 65 | } 66 | /// 67 | /// Start training the feature extractor 68 | /// 69 | public async Task TrainAsync() 70 | { 71 | await _featureExtractor.CallVoidAsync("train",(JSCallback)WhileTrainingCallback); 72 | } 73 | 74 | #nullable disable 75 | /// 76 | /// Get result of classification 77 | /// 78 | /// Optional. An HTML image or video element or a p5 image or video element. If not input is provided, the video element provided in the method-type will be used. 79 | /// 80 | public async Task ClassifyAsync(object input = null) 81 | { 82 | if (input is null) 83 | await _featureExtractor.CallVoidAsync("classify",(JSCallback)OnClassifyCallback); 84 | else 85 | await _featureExtractor.CallVoidAsync("classify", input,(JSCallback)OnClassifyCallback); 86 | } 87 | /// 88 | /// Get result of regression on a video 89 | /// 90 | /// Optional. An HTML image or video element or a p5 image or video element. If not input is provided, the video element provided in the method-type will be used. 91 | /// 92 | public async Task PredictAsync(object input = null) 93 | { 94 | if (input is null) 95 | await _featureExtractor.CallVoidAsync("predict",(JSCallback)OnPredictionCallback); 96 | else 97 | await _featureExtractor.CallVoidAsync("predict", input,(JSCallback)OnPredictionCallback); 98 | } 99 | /// 100 | /// [Undocumented] Get features image snapshots of video 101 | /// 102 | /// input video 103 | /// Logit object 104 | public async Task InferAsync(T input) 105 | { 106 | JObjPtr ptr= await _featureExtractor.CallRefAsync("infer", input); 107 | return new Logits(ptr); 108 | } 109 | #nullable restore 110 | 111 | 112 | public async Task SaveAsync(string? name=null) 113 | { 114 | if (name is null) 115 | await _featureExtractor.CallVoidAsync("save",(JSCallback)SaveCallback); 116 | else 117 | await _featureExtractor.CallVoidAsync("save",(JSCallback)SaveCallback, name); 118 | } 119 | 120 | public async Task LoadAsync(string? name=null) 121 | { 122 | if (name is null) 123 | await _featureExtractor.CallVoidAsync("load",(JSCallback)LoadCallback); 124 | else 125 | await _featureExtractor.CallVoidAsync("load",name,(JSCallback)LoadCallback ); 126 | } 127 | public async Task LoadAsync(string[]? name=null) 128 | { 129 | if (name is null) 130 | await _featureExtractor.CallVoidAsync("load",(JSCallback)LoadCallback); 131 | else 132 | await _featureExtractor.CallVoidAsync("load",name,(JSCallback)LoadCallback ); 133 | } 134 | 135 | public delegate void ModelLoadedHandler(); 136 | public event ModelLoadedHandler? OnModelLoaded; 137 | 138 | public delegate void VideoLoadedHandler(); 139 | public event VideoLoadedHandler? OnVideoLoaded; 140 | 141 | public delegate void ImageAddedHandler(); 142 | public event ImageAddedHandler? OnImageAdded; 143 | 144 | public delegate void OnTrainingHandler(double loss); 145 | public event OnTrainingHandler? OnTraining; 146 | 147 | public delegate void DoneTrainingHandler(); 148 | public event DoneTrainingHandler? OnTrainingFinished; 149 | 150 | public delegate void SaveHandler(); 151 | public event SaveHandler? OnSave; 152 | 153 | public delegate void LoadHandler(); 154 | public event LoadHandler? OnLoad; 155 | 156 | public delegate void OnClassificationHandler(ClassificationResult[] result); 157 | public event OnClassificationHandler? OnClassify; 158 | public delegate void OnPredictionHandler(PredictionResult result); 159 | public event OnPredictionHandler? OnPredict; 160 | 161 | private void SaveCallback(JObjPtr[] _) 162 | { 163 | OnSave?.Invoke(); 164 | } 165 | private void LoadCallback(JObjPtr[] _) 166 | { 167 | OnLoad?.Invoke(); 168 | } 169 | private void OnImageAddedCallback(JObjPtr[] _) 170 | { 171 | OnImageAdded?.Invoke(); 172 | } 173 | private void OnVideoLoadedCallback(JObjPtr[] _) 174 | { 175 | OnVideoLoaded?.Invoke(); 176 | } 177 | internal void OnModelLoadCallback(JObjPtr[] _) 178 | { 179 | OnModelLoaded?.Invoke(); 180 | } 181 | private void OnClassifyCallback(JObjPtr[] args) 182 | { 183 | if (OnClassify is null) return; 184 | OnClassify?.Invoke(args[1].To()); 185 | } 186 | private void OnPredictionCallback(JObjPtr[] args) 187 | { 188 | if (OnPredict is null) return; 189 | OnPredict?.Invoke(args[1].To()); 190 | } 191 | 192 | private void WhileTrainingCallback(JObjPtr[] args) 193 | { 194 | if(OnTraining is null && OnTrainingFinished is null) 195 | return; 196 | var arg0=args[0].To(); 197 | if(string.IsNullOrWhiteSpace(arg0)) 198 | OnTrainingFinished?.Invoke(); 199 | else 200 | { 201 | double.TryParse(arg0,out double num); 202 | OnTraining?.Invoke(num); 203 | } 204 | } 205 | 206 | } 207 | 208 | public record Logits 209 | { 210 | /// 211 | /// Exposes raw js object that represents logits as tensor 212 | /// 213 | public readonly JObjPtr _logits; 214 | internal Logits(JObjPtr logits) 215 | { 216 | _logits = logits; 217 | } 218 | } -------------------------------------------------------------------------------- /BlazorML5/Helpers/KnnClassifier.cs: -------------------------------------------------------------------------------- 1 | using BlazorBindGen; 2 | 3 | namespace BlazorML5.Helpers; 4 | 5 | public class KnnClassifier 6 | { 7 | private readonly JObjPtr _knn; 8 | internal KnnClassifier(JObjPtr knn) 9 | { 10 | _knn = knn; 11 | } 12 | 13 | /// 14 | /// Adding an example to a class. 15 | /// 16 | /// An example to add to the dataset, usually an activation from another model 17 | /// String | Number. The class index(number) or label(string) of the example. 18 | /// 19 | /// 20 | public async Task AddExampleAsync(T example, TS indexOrLabel) 21 | { 22 | if(example is Logits l) 23 | await _knn.CallVoidAsync("addExample", l._logits, indexOrLabel!); 24 | else 25 | await _knn.CallVoidAsync("addExample", example!, indexOrLabel!); 26 | } 27 | /// 28 | /// Classify an new input. 29 | /// 30 | /// . An example to make a prediction on, could be an activation from another model or an array of numbers. 31 | /// The K value to use in K-nearest neighbors. The algorithm will first find the K nearest examples from those it was previously shown, and then choose the class that appears the most as the final prediction for the input example. 32 | /// 33 | public async Task ClassifyAsync(T input, int k = 3) 34 | { 35 | if(input is Logits l) 36 | await _knn.CallVoidAsync("classify", l._logits,k,(JSCallback)OnClassifyCallback); 37 | else 38 | await _knn.CallVoidAsync("classify", input!, k,(JSCallback)OnClassifyCallback); 39 | } 40 | /// 41 | /// Clears the specified label. 42 | /// 43 | /// The class index or label, a number or a string. 44 | /// 45 | public async Task ClearLabelAsync(T indexOrLabel) 46 | { 47 | await _knn.CallVoidAsync("clearLabel", indexOrLabel!); 48 | } 49 | /// 50 | /// Clear all examples in all labels 51 | /// 52 | public async Task ClearAllLabelsAsync() 53 | { 54 | await _knn.CallVoidAsync("clearAllLabels"); 55 | } 56 | /// 57 | /// Get the example count for each label. It returns an object that maps class label to example count for each class. 58 | /// 59 | /// 60 | public async Task> GetCountByLabelAsync() 61 | { 62 | return await _knn.CallAsync>("getCountByLabel"); 63 | } 64 | /// 65 | /// Get the example count for each class. It returns an object that maps class index to example count for each class. 66 | /// 67 | /// 68 | public async Task> GetCountAsync() 69 | { 70 | return await _knn.CallAsync>("getCount"); 71 | } 72 | /// 73 | /// It returns the total number of labels. 74 | /// 75 | /// 76 | public async Task GetNumLabelsAsync() 77 | { 78 | return await _knn.CallAsync("getNumLabels"); 79 | } 80 | /// 81 | /// Download the whole dataset as a JSON file. It's useful for saving state. 82 | /// 83 | /// 84 | public async Task SaveAsync(string? path=null) 85 | { 86 | if(path is null) 87 | await _knn.CallVoidAsync("save"); 88 | else 89 | await _knn.CallVoidAsync("save",path); 90 | } 91 | /// 92 | /// Load a dataset from a JSON file. It's useful for restoring state. 93 | /// 94 | /// 95 | public async Task LoadAsync(string url) 96 | { 97 | await _knn.CallVoidAsync("load",url,(JSCallback)OnDataLoadCallback); 98 | } 99 | 100 | public delegate void OnDataLoadHandler(); 101 | public event OnDataLoadHandler? OnDataLoad; 102 | 103 | public delegate void OnClassfifyHandler(KnnResult result); 104 | public event OnClassfifyHandler? OnClassify; 105 | 106 | private void OnClassifyCallback(JObjPtr[] args) 107 | { 108 | if(OnClassify is null) return; 109 | OnClassify?.Invoke(args[1].To()); 110 | } 111 | 112 | private void OnDataLoadCallback(JObjPtr[] _) 113 | { 114 | OnDataLoad?.Invoke(); 115 | } 116 | } 117 | public record KnnResult(string Label,int ClassIndex,IReadOnlyDictionary Confidences,IReadOnlyDictionary ConfidencesByLabel); -------------------------------------------------------------------------------- /BlazorML5/Helpers/NeuralNetwork.cs: -------------------------------------------------------------------------------- 1 | using BlazorBindGen; 2 | namespace BlazorML5.Helpers; 3 | 4 | public class NeuralNetwork 5 | { 6 | #nullable disable 7 | /// 8 | /// Pointer to JS Neural Network Object 9 | /// 10 | private JObjPtr _neuralNetwork; 11 | 12 | /// 13 | /// set to true if the model is loaded and ready, false if it is not. 14 | /// 15 | public ValueTask Ready => _neuralNetwork.PropValAsync("ready"); 16 | 17 | internal NeuralNetwork() { } 18 | #nullable restore 19 | internal async Task InitAsync(JObjPtr neuralNetwork) 20 | { 21 | _neuralNetwork = neuralNetwork; 22 | await _neuralNetwork.SetPropCallBackAsync("callback", (_) => OnDataLoaded?.Invoke(this)); 23 | return this; 24 | } 25 | 26 | /// 27 | /// Add data to the neural network for training purpose. 28 | /// 29 | /// features can be a array ,number objects 30 | /// labels can be array or number 31 | public async Task AddDataAsync(T[] xs, TS[] ys) 32 | { 33 | await _neuralNetwork.CallVoidAsync("addData", xs, ys ); 34 | } 35 | 36 | /// 37 | /// normalizes the data stored in the neural network. 38 | /// 39 | public async Task NormalizeDataAsync() 40 | { 41 | await _neuralNetwork.CallVoidAsync("normalizeData"); 42 | } 43 | 44 | /// 45 | /// Start training on store data 46 | /// 47 | /// Specify training options like epochs etc 48 | public async Task TrainAsync(NeuralNetworkTrainOptions? options = default) 49 | { 50 | options??=new NeuralNetworkTrainOptions(); 51 | await _neuralNetwork.CallVoidAsync("train" 52 | ,options,(JSCallback)OnTrainingCallback 53 | ,(JSCallback)OnTrainingEndCallback); 54 | } 55 | /// 56 | /// Start prediction on trained data,subscribe to the event to get the result 57 | /// 58 | /// input array or object 59 | public async Task PredictAsync(T[] xs) 60 | { 61 | await _neuralNetwork.CallVoidAsync("predict", xs,(JSCallback)OnPredictCallback); 62 | } 63 | /// 64 | /// Perform multiple prediction on trained data,use only when you have more than 1 objects, subscribe to the event to get the result 65 | /// 66 | /// array of array or array of objects 67 | /// 68 | public async Task PredictMultipleAsync(T[] xs) 69 | { 70 | await _neuralNetwork.CallVoidAsync("predictMultiple", xs,(JSCallback)OnPredictMultipleCallback); 71 | } 72 | /// 73 | /// Perform classification on trained data, subscribe to the event to get the result 74 | /// 75 | /// array or object 76 | /// 77 | public async Task ClassifyAsync(T[] xs) 78 | { 79 | await _neuralNetwork.CallVoidAsync("classify", xs,(JSCallback)OnClassifyCallback); 80 | } 81 | /// 82 | /// Perform multiple classification on trained data,use only when you have more than 1 objects, subscribe to the event to get the result 83 | /// 84 | /// array of array or array of objects 85 | /// 86 | public async Task ClassifyMultipleAsync(T[] xs) 87 | { 88 | await _neuralNetwork.CallVoidAsync("classifyMultiple", xs,(JSCallback)OnClassifyMultipleCallback); 89 | } 90 | 91 | /// 92 | /// Saves the data that has been added 93 | /// 94 | /// :Optional. String. An output name you'd like your data to be called. If no input is given, then the name will be data_YYYY-MM-DD_mm-hh 95 | public async Task SaveDataAsync(string? path = null) 96 | { 97 | if(path is null) 98 | await _neuralNetwork.CallVoidAsync("saveData",(JSCallback)OnDataSavedCallback); 99 | else 100 | await _neuralNetwork.CallVoidAsync("saveData", path,(JSCallback)OnDataSavedCallback); 101 | } 102 | /// 103 | /// Load the data that has been saved 104 | /// 105 | /// REQUIRED. String | InputFiles. A string path to a .json data object or InputFiles from html input type="file". Must be structured for example as: {"data": [ { xs:{input0:1, input1:2}, ys:{output0:"a"}, ...]} 106 | public async Task LoadDataAsync(string path) 107 | { 108 | await _neuralNetwork.CallVoidAsync("loadData", path,(JSCallback)OnDataLoadedCallback); 109 | } 110 | /// 111 | /// Save trained Neural Network Model 112 | /// 113 | /// 114 | public async Task SaveAsync(string? outputName=null) 115 | { 116 | if(outputName is null) 117 | await _neuralNetwork.CallVoidAsync("save",(JSCallback)OnModelSavedCallback); 118 | else 119 | await _neuralNetwork.CallVoidAsync("save", outputName,(JSCallback)OnModelSavedCallback); 120 | } 121 | /// 122 | /// Load a pre-trained Neural Network Model 123 | /// 124 | /// 125 | public async Task LoadAsync(string[] path) 126 | { 127 | await _neuralNetwork.CallVoidAsync("load", path,(JSCallback)OnModelLoadCallback); 128 | } 129 | /// 130 | /// Load a pre-trained Neural Network Model 131 | /// 132 | /// 133 | public async Task LoadAsync(string path) 134 | { 135 | await _neuralNetwork.CallVoidAsync("load", path,(JSCallback)OnModelLoadCallback); 136 | } 137 | 138 | public delegate void OnLoadedHandler(NeuralNetwork neuralNetwork); 139 | 140 | 141 | /// 142 | /// Fires when data is loaded and ready to use. 143 | /// 144 | public event OnLoadedHandler? OnDataLoaded; 145 | 146 | public delegate void DoneTrainingHandler(); 147 | 148 | /// 149 | /// Fires when training is done. 150 | /// 151 | public event DoneTrainingHandler? OnTrainingComplete; 152 | 153 | public delegate void WhileTrainingHandler(int epoch,double loss); 154 | /// 155 | /// Fires when training is in progress. 156 | /// 157 | public event WhileTrainingHandler? OnTraining; 158 | 159 | public delegate void OnDataSaveHandler(); 160 | /// 161 | /// Fires when data in Neural Network is saved locally. 162 | /// 163 | public event OnDataSaveHandler? OnDataSaved; 164 | 165 | public delegate void OnPredictHandler(string error,PredictionResult[] predictions); 166 | 167 | /// 168 | /// Fires when prediction is done. (Task Regression) 169 | /// 170 | public event OnPredictHandler? OnPredict; 171 | public delegate void OnClassifyHandler(string error,ClassificationResult[] predictions); 172 | /// 173 | /// Fires when classification is done. (Task Classification) 174 | /// 175 | public event OnClassifyHandler? OnClassify; 176 | public delegate void OnClassifyMultipleHandler(string error,ClassificationResult[][] predictions); 177 | /// 178 | /// FIres when result of multiple classification is available (Task Classification) 179 | /// 180 | public event OnClassifyMultipleHandler? OnClassifyMultiple; 181 | public delegate void OnPredictMultipleHandler(string err,PredictionResult[][] predictions); 182 | /// 183 | /// Fires when result of multiple prediction is available (Task Regression) 184 | /// 185 | public event OnPredictMultipleHandler? OnPredictMultiple; 186 | 187 | public delegate void OnModelSavedHandler(); 188 | /// 189 | /// Fires when model is saved. 190 | /// 191 | public event OnModelSavedHandler? OnModelSaved; 192 | public delegate void OnModelLoadHandler(); 193 | /// 194 | /// Fires when model is loaded. 195 | /// 196 | public event OnModelLoadHandler? OnModelLoaded; 197 | 198 | 199 | private void OnPredictCallback(JObjPtr[] result) 200 | { 201 | if(OnPredict==null) return; 202 | var err=result[0].To(); 203 | var predictions=result[1].To(); 204 | OnPredict?.Invoke(err,predictions); 205 | } 206 | private void OnPredictMultipleCallback(JObjPtr[] result) 207 | { 208 | if(OnPredictMultiple==null) return; 209 | var err=result[0].To(); 210 | var predictions=result[1].To(); 211 | OnPredictMultiple?.Invoke(err,predictions); 212 | } 213 | private void OnClassifyMultipleCallback(JObjPtr[] result) 214 | { 215 | if(OnClassifyMultiple==null) return; 216 | var err=result[0].To(); 217 | var predictions=result[1].To(); 218 | OnClassifyMultiple?.Invoke(err,predictions); 219 | } 220 | private void OnClassifyCallback(JObjPtr[] result) 221 | { 222 | if(OnClassify==null) return; 223 | var err=result[0].To(); 224 | var predictions=result[1].To(); 225 | OnClassify?.Invoke(err,predictions); 226 | } 227 | internal void OnDataLoadedCallback(JObjPtr[] _) 228 | { 229 | OnDataLoaded?.Invoke(this); 230 | } 231 | 232 | private async void OnTrainingCallback(JObjPtr[] obj) 233 | { 234 | if(OnTraining==null) return; 235 | var epoch = obj[0].To(); 236 | var loss = await obj[1].PropValAsync("loss"); 237 | OnTraining.Invoke(epoch,loss); 238 | } 239 | private void OnTrainingEndCallback(JObjPtr[] obj) 240 | { 241 | OnTrainingComplete?.Invoke(); 242 | } 243 | private void OnDataSavedCallback(JObjPtr[] obj) 244 | { 245 | OnDataSaved?.Invoke(); 246 | } 247 | private void OnModelSavedCallback(JObjPtr[] obj) 248 | { 249 | OnModelSaved?.Invoke(); 250 | } 251 | private void OnModelLoadCallback(JObjPtr[] obj) 252 | { 253 | OnModelLoaded?.Invoke(); 254 | } 255 | } -------------------------------------------------------------------------------- /BlazorML5/Helpers/NeuralNetworkOptions.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | using BlazorBindGen; 3 | 4 | namespace BlazorML5.Helpers; 5 | 6 | public record NeuralNetworkOptions 7 | { 8 | /// 9 | ///Inputs Can be a number or any array of numbers. 10 | /// 11 | public object? Inputs { get; init; } 12 | 13 | /// 14 | /// Outputs can be a number or any array of numbers. 15 | /// 16 | public object? Outputs { get; init; } 17 | 18 | /// 19 | /// Url from where to get the model. 20 | /// 21 | public string? DataUrl { get; init; } = null; 22 | 23 | /// 24 | /// Url of pretrained model to load. 25 | /// 26 | public string? ModelUrl { get; init; } = null; 27 | 28 | /// 29 | /// if you want custom layers specify an anonymous object with matching ml5.js custom layer structure. 30 | /// 31 | public object[]? Layers { get; init; } 32 | 33 | /// 34 | /// Kind of task this neural Network is used for. 'classification', 'regression', 'imageClassification' 35 | /// 36 | public TaskType? Task { get; init; } = null; 37 | 38 | /// 39 | /// determines whether or not to show the training visualization 40 | /// 41 | public bool Debug { get; init; } = false; 42 | 43 | public double LearningRate { get; init; } = 0.2; 44 | 45 | public int HiddenUnits { get; init; } = 16; 46 | 47 | internal async Task EliminateNullPropObject() 48 | { 49 | var obj = await BindGen.Window.CallRefAsync("Object"); 50 | if(Inputs!=null) 51 | await obj.SetPropValAsync("inputs", Inputs); 52 | if (Outputs != null) 53 | await obj.SetPropValAsync("outputs", Outputs); 54 | if (DataUrl != null) 55 | await obj.SetPropValAsync("dataUrl", DataUrl); 56 | if(ModelUrl!=null) 57 | await obj.SetPropValAsync("modelUrl", ModelUrl); 58 | if(Layers!=null) 59 | await obj.SetPropValAsync("layers", Layers); 60 | if(this.Task!=null) 61 | await obj.SetPropValAsync("task",UtilHelper.FirstCharSmall(Enum.GetName(typeof(TaskType), this.Task)!)); 62 | 63 | await obj.SetPropValAsync("debug", Debug); 64 | await obj.SetPropValAsync("learningRate", LearningRate); 65 | await obj.SetPropValAsync("hiddenUnits", HiddenUnits); 66 | 67 | return obj; 68 | } 69 | } 70 | 71 | /// 72 | /// Represents kind of task that the neural network should perform. 73 | /// 74 | public enum TaskType 75 | { 76 | Regression, 77 | Classification, 78 | ImageClassification 79 | } 80 | 81 | public record NeuralNetworkTrainOptions 82 | { 83 | [JsonPropertyName("batchSize")] 84 | public int BatchSize { get; init; } = 32; 85 | [JsonPropertyName("epochs")] 86 | public int Epochs { get; init; } = 16; 87 | } 88 | 89 | public record PredictionResult(double Value, string Label); 90 | public record ClassificationResult(double Confidence,string Label); -------------------------------------------------------------------------------- /BlazorML5/Helpers/UtilHelper.cs: -------------------------------------------------------------------------------- 1 | namespace BlazorML5.Helpers; 2 | 3 | internal static class UtilHelper 4 | { 5 | public static string FirstCharSmall(string str) 6 | { 7 | if (string.IsNullOrEmpty(str)) 8 | return string.Empty; 9 | return str.Substring(0, 1).ToLower() + str.Substring(1); 10 | } 11 | } -------------------------------------------------------------------------------- /BlazorML5/Image/FaceMesh.cs: -------------------------------------------------------------------------------- 1 | using BlazorBindGen; 2 | 3 | namespace BlazorML5.Image; 4 | 5 | public class FaceMesh 6 | { 7 | #nullable disable 8 | private JObjPtr _faceMesh; 9 | internal FaceMesh() { } 10 | internal FaceMesh Init(JObjPtr faceMesh) 11 | { 12 | _faceMesh = faceMesh; 13 | return this; 14 | } 15 | 16 | #nullable restore 17 | 18 | public async Task PredictAsync(object? media = null) 19 | { 20 | if( media == null ) 21 | { 22 | await _faceMesh.CallVoidAsync("predict",(JSCallback)OnPredictCallback); 23 | } 24 | else 25 | { 26 | await _faceMesh.CallVoidAsync("predict", media,(JSCallback)OnPredictCallback); 27 | } 28 | } 29 | 30 | private void OnPredictCallback(JObjPtr[] args) 31 | { 32 | if(OnPredict is null) return; 33 | var res = args[0].To(); 34 | OnPredict(res); 35 | } 36 | 37 | public delegate void OnModelLoadHandler(); 38 | public event OnModelLoadHandler? OnModelLoad; 39 | 40 | public delegate void OnPredictHandler(FaceResult[] results); 41 | public event OnPredictHandler? OnPredict; 42 | 43 | internal void OnModelLoadCallback(JObjPtr[] args) 44 | { 45 | OnModelLoad?.Invoke(); 46 | } 47 | } -------------------------------------------------------------------------------- /BlazorML5/Image/FaceMeshOptions.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | 3 | namespace BlazorML5.Image; 4 | 5 | public class BoundingBox 6 | { 7 | [JsonPropertyName("topLeft")] 8 | public List>? TopLeft { get; set; } 9 | 10 | [JsonPropertyName("bottomRight")] 11 | public List>? BottomRight { get; set; } 12 | } 13 | 14 | public class Annotations 15 | { 16 | [JsonPropertyName("silhouette")] 17 | public List>? Silhouette { get; set; } 18 | 19 | [JsonPropertyName("lipsUpperOuter")] 20 | public List>? LipsUpperOuter { get; set; } 21 | 22 | [JsonPropertyName("lipsLowerOuter")] 23 | public List>? LipsLowerOuter { get; set; } 24 | 25 | [JsonPropertyName("lipsUpperInner")] 26 | public List>? LipsUpperInner { get; set; } 27 | 28 | [JsonPropertyName("lipsLowerInner")] 29 | public List>? LipsLowerInner { get; set; } 30 | 31 | [JsonPropertyName("rightEyeUpper0")] 32 | public List>? RightEyeUpper0 { get; set; } 33 | 34 | [JsonPropertyName("rightEyeLower0")] 35 | public List>? RightEyeLower0 { get; set; } 36 | 37 | [JsonPropertyName("rightEyeUpper1")] 38 | public List>? RightEyeUpper1 { get; set; } 39 | 40 | [JsonPropertyName("rightEyeLower1")] 41 | public List>? RightEyeLower1 { get; set; } 42 | 43 | [JsonPropertyName("rightEyeUpper2")] 44 | public List>? RightEyeUpper2 { get; set; } 45 | 46 | [JsonPropertyName("rightEyeLower2")] 47 | public List>? RightEyeLower2 { get; set; } 48 | 49 | [JsonPropertyName("rightEyeLower3")] 50 | public List>? RightEyeLower3 { get; set; } 51 | 52 | [JsonPropertyName("rightEyebrowUpper")] 53 | public List>? RightEyebrowUpper { get; set; } 54 | 55 | [JsonPropertyName("rightEyebrowLower")] 56 | public List>? RightEyebrowLower { get; set; } 57 | 58 | [JsonPropertyName("leftEyeUpper0")] 59 | public List>? LeftEyeUpper0 { get; set; } 60 | 61 | [JsonPropertyName("leftEyeLower0")] 62 | public List>? LeftEyeLower0 { get; set; } 63 | 64 | [JsonPropertyName("leftEyeUpper1")] 65 | public List>? LeftEyeUpper1 { get; set; } 66 | 67 | [JsonPropertyName("leftEyeLower1")] 68 | public List>? LeftEyeLower1 { get; set; } 69 | 70 | [JsonPropertyName("leftEyeUpper2")] 71 | public List>? LeftEyeUpper2 { get; set; } 72 | 73 | [JsonPropertyName("leftEyeLower2")] 74 | public List>? LeftEyeLower2 { get; set; } 75 | 76 | [JsonPropertyName("leftEyeLower3")] 77 | public List>? LeftEyeLower3 { get; set; } 78 | 79 | [JsonPropertyName("leftEyebrowUpper")] 80 | public List>? LeftEyebrowUpper { get; set; } 81 | 82 | [JsonPropertyName("leftEyebrowLower")] 83 | public List>? LeftEyebrowLower { get; set; } 84 | 85 | [JsonPropertyName("midwayBetweenEyes")] 86 | public List>? MidwayBetweenEyes { get; set; } 87 | 88 | [JsonPropertyName("noseTip")] 89 | public List>? NoseTip { get; set; } 90 | 91 | [JsonPropertyName("noseBottom")] 92 | public List>? NoseBottom { get; set; } 93 | 94 | [JsonPropertyName("noseRightCorner")] 95 | public List>? NoseRightCorner { get; set; } 96 | 97 | [JsonPropertyName("noseLeftCorner")] 98 | public List>? NoseLeftCorner { get; set; } 99 | 100 | [JsonPropertyName("rightCheek")] 101 | public List>? RightCheek { get; set; } 102 | 103 | [JsonPropertyName("leftCheek")] 104 | public List>? LeftCheek { get; set; } 105 | } 106 | 107 | public class FaceResult 108 | { 109 | [JsonPropertyName("faceInViewConfidence")] 110 | public double FaceInViewConfidence { get; set; } 111 | 112 | [JsonPropertyName("boundingBox")] 113 | public BoundingBox? BoundingBox { get; set; } 114 | 115 | [JsonPropertyName("mesh")] 116 | public List>? Mesh { get; set; } 117 | 118 | [JsonPropertyName("scaledMesh")] 119 | public List>? ScaledMesh { get; set; } 120 | 121 | [JsonPropertyName("annotations")] 122 | public Annotations? Annotations { get; set; } 123 | } 124 | 125 | public record FaceMeshOptions 126 | { 127 | public bool FlipHorizontal { get; init; } = false; 128 | public int MaxContinuousChecks { get; init; } = 5; 129 | public double DetectionConfidence { get; init; }= 0.9; 130 | public int MaxFaces { get; init; } = 10; 131 | public double ScoreThreshold { get; init; } =0.75; 132 | public double IouThreshold { get; init; } = 0.3; 133 | } 134 | 135 | -------------------------------------------------------------------------------- /BlazorML5/Image/ImageClassifier.cs: -------------------------------------------------------------------------------- 1 | using BlazorBindGen; 2 | using BlazorML5.Helpers; 3 | 4 | namespace BlazorML5.Image; 5 | 6 | public class ImageClassifier 7 | { 8 | #nullable disable 9 | private JObjPtr _imageClassifier; 10 | internal ImageClassifier(){} 11 | #nullable restore 12 | 13 | public ValueTask ModelName=>_imageClassifier.PropValAsync("modelName"); 14 | public ValueTask ModelUrl=>_imageClassifier.PropValAsync("modelUrl"); 15 | internal ImageClassifier Init(JObjPtr imageClassifier) 16 | { 17 | _imageClassifier = imageClassifier; 18 | return this; 19 | } 20 | public async Task ClassifyAsync(T input,int? numOfClasses=null) 21 | { 22 | if(numOfClasses is null) 23 | await _imageClassifier.CallVoidAsync("classify",input!,(JSCallback)OnClassifyCallback); 24 | else 25 | await _imageClassifier.CallVoidAsync("classify",input!,numOfClasses,(JSCallback)OnClassifyCallback); 26 | } 27 | 28 | public delegate void ModelLoadedHandler(); 29 | 30 | public event ModelLoadedHandler? OnModelLoad; 31 | public event NeuralNetwork.OnClassifyHandler? OnClassify; 32 | internal void OnModelLoadCallback(JObjPtr[] _) 33 | { 34 | OnModelLoad?.Invoke(); 35 | } 36 | private void OnClassifyCallback(JObjPtr[] args) 37 | { 38 | if(OnClassify is null) return; 39 | OnClassify.Invoke(args[0].To() 40 | ,args[1].To()); 41 | } 42 | } 43 | public enum ImageModel 44 | { 45 | MobileNet, 46 | Darknet, 47 | DarknetTiny, 48 | DoodleNet 49 | } 50 | 51 | public static class ImageModelExtension 52 | { 53 | public static string GetName(this ImageModel model) 54 | { 55 | switch (model) 56 | { 57 | case ImageModel.MobileNet: 58 | return "MobileNet"; 59 | case ImageModel.Darknet: 60 | return "Darknet"; 61 | case ImageModel.DarknetTiny: 62 | return "Darknet-tiny"; 63 | case ImageModel.DoodleNet: 64 | return "DoodleNet"; 65 | default: 66 | throw new ArgumentOutOfRangeException(nameof(model), model, null); 67 | } 68 | } 69 | } 70 | 71 | public record ImageClassifierOptions(int Version=1,double Alpha=1.0,int TopK=3); -------------------------------------------------------------------------------- /BlazorML5/Image/ObjectDetector.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | using BlazorBindGen; 3 | using BlazorML5.Text; 4 | 5 | namespace BlazorML5.Image; 6 | 7 | public class ObjectDetector 8 | { 9 | #nullable disable 10 | private JObjPtr _objectDetector; 11 | internal ObjectDetector() 12 | { 13 | } 14 | public ObjectDetector Init(JObjPtr ptr) 15 | { 16 | _objectDetector = ptr; 17 | return this; 18 | } 19 | #nullable restore 20 | 21 | 22 | 23 | public async Task DetectAsync(T input) 24 | { 25 | await _objectDetector.CallVoidAsync("detect",input!,(JSCallback)OnDetectCallback); 26 | } 27 | 28 | public delegate void OnModelLoadHandler(); 29 | public event OnModelLoadHandler? OnModelLoad; 30 | public delegate void OnDetectHandler(string err, ObjectResult[] result); 31 | public event OnDetectHandler? OnDetect; 32 | internal void OnModelLoadCallback(JObjPtr[] obj) 33 | { 34 | OnModelLoad?.Invoke(); 35 | } 36 | 37 | private void OnDetectCallback(JObjPtr[] args) 38 | { 39 | if (OnDetect is null) return; 40 | var err = args[0].To(); 41 | var res=args[1].To(); 42 | OnDetect?.Invoke(err,res); 43 | } 44 | 45 | 46 | } 47 | 48 | public record ObjectDetectorOptions 49 | { 50 | public double FilterBoxesThreshold { get; init; } = 0.01; 51 | [JsonPropertyName("IOUThreshold")] 52 | public double IouThreshold { get; init; } = 0.4; 53 | public double ClassProbThreshold { get; init; } = 0.4; 54 | } 55 | public enum ObjectDetectorModel 56 | { 57 | CocoSsd, 58 | Yolo 59 | } 60 | public record ObjectBox 61 | { 62 | public double X { get; init; } 63 | public double Y { get; init; } 64 | public double Width { get; init; } 65 | public double Height { get; init; } 66 | } 67 | public class ObjectResult 68 | { 69 | public string? Label { get; init; } 70 | public double X { get; init; } 71 | public double Y { get; init; } 72 | public double Width { get; init; } 73 | public double Height { get; init; } 74 | public ObjectBox? Normalized { get; init; } 75 | } -------------------------------------------------------------------------------- /BlazorML5/Image/PoseNet.cs: -------------------------------------------------------------------------------- 1 | using BlazorBindGen; 2 | 3 | namespace BlazorML5.Image; 4 | 5 | public class PoseNet 6 | { 7 | #nullable disable 8 | private JObjPtr _poseNet; 9 | internal PoseNet(){} 10 | internal async Task InitAsync(JObjPtr poseNet) 11 | { 12 | _poseNet = poseNet; 13 | await _poseNet.CallVoidAsync("on", "pose", (JSCallback) OnPoseCallback); 14 | return this; 15 | } 16 | /// 17 | /// Get Pose Estimation 18 | /// 19 | /// A HTML video or image element or a p5 image or video element. If no input is provided, the default is to use the video given in the constructor. 20 | /// 21 | public async Task SinglePoseAsync(object canvasOrVideoOrImage=null) 22 | { 23 | if(canvasOrVideoOrImage is not null) 24 | await _poseNet.CallVoidAsync("singlePose",canvasOrVideoOrImage); 25 | else 26 | await _poseNet.CallVoidAsync("singlePose"); 27 | 28 | } 29 | /// 30 | /// Get multiple poses 31 | /// 32 | /// A HTML video or image element or a p5 image or video element. If no input is provided, the default is to use the video given in the constructor. 33 | /// 34 | public async Task MultiPoseAsync(object canvasOrVideoOrImage = null) 35 | { 36 | if(canvasOrVideoOrImage is not null) 37 | await _poseNet.CallVoidAsync("multiPose",canvasOrVideoOrImage); 38 | else 39 | await _poseNet.CallVoidAsync("multiPose"); 40 | } 41 | #nullable restore 42 | 43 | public delegate void OnModelLoadHandler(); 44 | public event OnModelLoadHandler? OnModelLoad; 45 | public delegate void OnPoseHandler(IReadOnlyList result); 46 | public event OnPoseHandler? OnPose; 47 | internal void OnModelLoadCallback(JObjPtr[] _) 48 | { 49 | OnModelLoad?.Invoke(); 50 | } 51 | private void OnPoseCallback(JObjPtr[] args) 52 | { 53 | OnPose?.Invoke(args[0].To>()); 54 | } 55 | } -------------------------------------------------------------------------------- /BlazorML5/Image/PoseNetOptions.cs: -------------------------------------------------------------------------------- 1 | namespace BlazorML5.Image; 2 | 3 | public class PoseNetOptions 4 | { 5 | public string Architecture { get; set; } = "MobileNetV1"; 6 | public double ImageScaleFactor { get; set; } = 0.3; 7 | public int OutputStride { get; set; } = 16; 8 | public bool FlipHorizontal { get; set; } = false; 9 | public double MinConfidence { get; set; } = 0.5; 10 | public int MaxPoseDetections { get; set; } = 5; 11 | public double ScoreThreshold { get; set; } = 0.5; 12 | public int NmsRadius { get; set; } = 20; 13 | public string DetectionType { get; set; } = "multiple"; 14 | public int InputResolution { get; set; } = 513; 15 | public double Multiplier { get; set; } = 0.75; 16 | public int QuantBytes { get; set; } = 2; 17 | } 18 | 19 | // Root myDeserializedClass = JsonConvert.DeserializeObject(myJsonResponse); 20 | 21 | 22 | public class Position { 23 | public double X { get; set; } 24 | public double Y { get; set; } 25 | } 26 | 27 | public class Keypoint { 28 | public double Score { get; set; } 29 | public string? Part { get; set; } 30 | public Position? Position { get; set; } 31 | } 32 | 33 | public class Nose { 34 | public double X { get; set; } 35 | public double Y { get; set; } 36 | public double Confidence { get; set; } 37 | } 38 | 39 | public class LeftEye { 40 | public double X { get; set; } 41 | public double Y { get; set; } 42 | public double Confidence { get; set; } 43 | } 44 | 45 | public class RightEye { 46 | public double X { get; set; } 47 | public double Y { get; set; } 48 | public double Confidence { get; set; } 49 | } 50 | 51 | public class LeftEar { 52 | public double X { get; set; } 53 | public double Y { get; set; } 54 | public double Confidence { get; set; } 55 | } 56 | 57 | public class RightEar { 58 | public double X { get; set; } 59 | public double Y { get; set; } 60 | public double Confidence { get; set; } 61 | } 62 | 63 | public class LeftShoulder { 64 | public double X { get; set; } 65 | public double Y { get; set; } 66 | public double Confidence { get; set; } 67 | } 68 | 69 | public class RightShoulder { 70 | public double X { get; set; } 71 | public double Y { get; set; } 72 | public double Confidence { get; set; } 73 | } 74 | 75 | public class LeftElbow { 76 | public double X { get; set; } 77 | public double Y { get; set; } 78 | public double Confidence { get; set; } 79 | } 80 | 81 | public class RightElbow { 82 | public double X { get; set; } 83 | public double Y { get; set; } 84 | public double Confidence { get; set; } 85 | } 86 | 87 | public class LeftWrist { 88 | public double X { get; set; } 89 | public double Y { get; set; } 90 | public double Confidence { get; set; } 91 | } 92 | 93 | public class RightWrist { 94 | public double X { get; set; } 95 | public double Y { get; set; } 96 | public double Confidence { get; set; } 97 | } 98 | 99 | public class LeftHip { 100 | public double X { get; set; } 101 | public double Y { get; set; } 102 | public double Confidence { get; set; } 103 | } 104 | 105 | public class RightHip { 106 | public double X { get; set; } 107 | public double Y { get; set; } 108 | public double Confidence { get; set; } 109 | } 110 | 111 | public class LeftKnee { 112 | public double X { get; set; } 113 | public double Y { get; set; } 114 | public double Confidence { get; set; } 115 | } 116 | 117 | public class RightKnee { 118 | public double X { get; set; } 119 | public double Y { get; set; } 120 | public double Confidence { get; set; } 121 | } 122 | 123 | public class LeftAnkle { 124 | public double X { get; set; } 125 | public double Y { get; set; } 126 | public double Confidence { get; set; } 127 | } 128 | 129 | public class RightAnkle { 130 | public double X { get; set; } 131 | public double Y { get; set; } 132 | public double Confidence { get; set; } 133 | } 134 | 135 | public class Pose { 136 | public double Score { get; set; } 137 | public IReadOnlyList? Keypoints { get; set; } 138 | public Nose? Nose { get; set; } 139 | public LeftEye? LeftEye { get; set; } 140 | public RightEye? RightEye { get; set; } 141 | public LeftEar? LeftEar { get; set; } 142 | public RightEar? RightEar { get; set; } 143 | public LeftShoulder? LeftShoulder { get; set; } 144 | public RightShoulder? RightShoulder { get; set; } 145 | public LeftElbow? LeftElbow { get; set; } 146 | public RightElbow? RightElbow { get; set; } 147 | public LeftWrist? LeftWrist { get; set; } 148 | public RightWrist? RightWrist { get; set; } 149 | public LeftHip? LeftHip { get; set; } 150 | public RightHip? RightHip { get; set; } 151 | public LeftKnee? LeftKnee { get; set; } 152 | public RightKnee? RightKnee { get; set; } 153 | public LeftAnkle? LeftAnkle { get; set; } 154 | public RightAnkle? RightAnkle { get; set; } 155 | } 156 | 157 | public class PoseResult { 158 | public Pose? Pose { get; set; } 159 | public IReadOnlyList>? Skeleton { get; set; } 160 | } -------------------------------------------------------------------------------- /BlazorML5/Ml5.cs: -------------------------------------------------------------------------------- 1 | using BlazorBindGen; 2 | using BlazorML5.Helpers; 3 | using BlazorML5.Image; 4 | using BlazorML5.Sound; 5 | using BlazorML5.Text; 6 | using Microsoft.JSInterop; 7 | 8 | namespace BlazorML5; 9 | 10 | public static class Ml5 11 | { 12 | /// 13 | /// Url of ML5 library 14 | /// 15 | private const string CdnMl5 = "https://unpkg.com/ml5@latest/dist/ml5.min.js"; 16 | 17 | #nullable disable 18 | /// 19 | /// Represent Ml5 object pointer in js runtime 20 | /// 21 | private static JObjPtr Ml5Ptr { get; set; } 22 | 23 | #nullable restore 24 | /// 25 | /// Initialize ML5 library 26 | /// 27 | /// Get reference to current pages IJSRuntime `@inject IJSRuntime runtime` and pass runtime here 28 | public static async ValueTask InitAsync(IJSRuntime runtime) 29 | { 30 | await BindGen.InitAsync(runtime); 31 | await BindGen.ImportAsync(CdnMl5); 32 | Ml5Ptr=await BindGen.Window.PropRefAsync("ml5"); 33 | } 34 | 35 | /// 36 | /// Creates a new neural network with options like input,output,hidden layers, etc. 37 | /// 38 | /// Specify configuration of neural network 39 | /// 40 | public static async Task NeuralNetworkAsync(NeuralNetworkOptions? options = null) 41 | { 42 | options ??= new NeuralNetworkOptions(); 43 | var nn = new NeuralNetwork(); 44 | var nnPtr = await Ml5Ptr.CallRefAsync("neuralNetwork", await options.EliminateNullPropObject() 45 | , (JSCallback)nn.OnDataLoadedCallback); 46 | return await nn.InitAsync(nnPtr); 47 | } 48 | /// 49 | /// Create a feature extractor 50 | /// 51 | /// model name or url 52 | /// configuration of model like learning rate etc. 53 | /// 54 | 55 | public static async Task FeatureExtractorAsync(string model = "MobileNet", object? options=null) 56 | { 57 | var fe = new FeatureExtractor(); 58 | JObjPtr ptr; 59 | if(options is null) 60 | ptr=await Ml5.Ml5Ptr.CallRefAsync("featureExtractor",model,(JSCallback)fe.OnModelLoadCallback); 61 | else 62 | ptr=await Ml5.Ml5Ptr.CallRefAsync("featureExtractor",model,options,(JSCallback)fe.OnModelLoadCallback); 63 | return fe.Init(ptr); 64 | } 65 | /// 66 | /// Create a KNN classifier 67 | /// 68 | /// 69 | public static async Task KnnClassifierAsync() 70 | { 71 | var ptr = await Ml5Ptr.CallRefAsync("KNNClassifier"); 72 | return new KnnClassifier(ptr); 73 | } 74 | 75 | /// 76 | /// Create a new Sentiment Analysis Classifier 77 | /// 78 | /// url of the model , defaults to movie review model 79 | /// 80 | public static async Task SentimentAsync(string model = "movieReviews") 81 | { 82 | Sentiment ss = new(); 83 | var sPtr = await Ml5.Ml5Ptr.CallRefAsync("sentiment", model, (JSCallback)ss.OnModelLoadedCallback); 84 | return ss.Init(sPtr); 85 | } 86 | 87 | /// 88 | /// is a method to create an object that classifies an image using a pre-trained model. 89 | /// 90 | /// A String value of a valid model OR a url to a model.json that contains a pre-trained model. 91 | /// Case insensitive. Models available are: 'MobileNet', 'Darknet' and 'Darknet-tiny','DoodleNet', or 92 | /// any image classification model trained in Teachable Machine. Below are some examples of creating a new image classifier: 93 | /// 94 | /// An object to change the defaults (shown below). The available options are 95 | /// 96 | public static async Task ImageClassifierAsync(string model,object? video=null, ImageClassifierOptions? options=null) 97 | { 98 | var ic = new ImageClassifier(); 99 | JObjPtr ptr; 100 | if (options is null && video is null) 101 | ptr = await Ml5.Ml5Ptr.CallRefAsync("imageClassifier", model, (JSCallback)ic.OnModelLoadCallback); 102 | else if(video is null) 103 | ptr = await Ml5.Ml5Ptr.CallRefAsync("imageClassifier", model, options!, (JSCallback)ic.OnModelLoadCallback); 104 | else if(options is null) 105 | ptr=await Ml5.Ml5Ptr.CallRefAsync("imageClassifier", model, video, (JSCallback)ic.OnModelLoadCallback); 106 | else 107 | ptr = await Ml5.Ml5Ptr.CallRefAsync("imageClassifier", model, video, options, (JSCallback)ic.OnModelLoadCallback); 108 | return ic.Init(ptr); 109 | } 110 | /// 111 | /// is a method to create an object that classifies an image using a pre-trained model. 112 | /// 113 | /// A String value of a valid model OR a url to a model.json that contains a pre-trained model. 114 | /// Case insensitive. Models available are: 'MobileNet', 'Darknet' and 'Darknet-tiny','DoodleNet', 115 | /// or any image classification model trained in Teachable Machine. Below are some examples of creating 116 | /// a new image classifier: 117 | /// 118 | /// An object to change the defaults (shown below). The available options are 119 | /// 120 | public static async Task ImageClassifierAsync(ImageModel model=ImageModel.MobileNet,object? video=null, ImageClassifierOptions? options=null) 121 | { 122 | return await ImageClassifierAsync(model.GetName(),video,options); 123 | } 124 | /// 125 | /// Create an instance of Pose detector 126 | /// 127 | /// 128 | /// 129 | /// 130 | public static async Task PoseNetAsync(object? video=null,PoseNetOptions? options=null) 131 | { 132 | var pn = new PoseNet(); 133 | JObjPtr ptr; 134 | if (options is null && video is null) 135 | ptr = await Ml5.Ml5Ptr.CallRefAsync("poseNet", (JSCallback)pn.OnModelLoadCallback); 136 | else if (video is null) 137 | ptr = await Ml5.Ml5Ptr.CallRefAsync("poseNet", options!, (JSCallback)pn.OnModelLoadCallback); 138 | else if (options is null) 139 | ptr = await Ml5.Ml5Ptr.CallRefAsync("poseNet", video, (JSCallback)pn.OnModelLoadCallback); 140 | else 141 | ptr = await Ml5.Ml5Ptr.CallRefAsync("poseNet", video, options, (JSCallback)pn.OnModelLoadCallback); 142 | return await pn.InitAsync(ptr); 143 | } 144 | /// 145 | /// Create an Object Detector Model with a url to a model.json that contains a pre-trained model. 146 | /// 147 | /// 148 | /// 149 | /// 150 | 151 | public static async Task ObjectDetectorAsync(string modelName,ObjectDetectorOptions? options=null) 152 | { 153 | var od = new ObjectDetector(); 154 | JObjPtr ptr; 155 | if (options is null) 156 | ptr = await Ml5.Ml5Ptr.CallRefAsync("objectDetector", modelName, (JSCallback)od.OnModelLoadCallback); 157 | else 158 | ptr = await Ml5.Ml5Ptr.CallRefAsync("objectDetector", modelName, options, (JSCallback)od.OnModelLoadCallback); 159 | return od.Init(ptr); 160 | } 161 | /// 162 | /// Create an object detector model with a pre-trained model. 163 | /// 164 | /// 165 | /// 166 | /// 167 | public static async Task ObjectDetectorAsync(ObjectDetectorModel model=ObjectDetectorModel.Yolo,ObjectDetectorOptions? options=null) 168 | { 169 | return await ObjectDetectorAsync(Enum.GetName(typeof(ObjectDetectorModel),model)!.ToUpper(),options); 170 | } 171 | /// 172 | /// allows you to classify audio. With the right pre-trained models, 173 | /// you can detect whether a certain noise was made (e.g. a clapping sound or a whistle) or a certain word was said ( 174 | /// e.g. Up, Down, Yes, No). At this moment, with the ml5.soundClassifier(), you can use your own custom pre-trained 175 | /// speech commands or use the the "SpeechCommands18w" which can recognize "the ten digits from "zero" to "nine", "up", 176 | /// "down", "left", "right", "go", "stop", "yes", "no", as well as the additional categories of "unknown word" and "background noise"." 177 | /// 178 | /// 179 | /// 180 | /// 181 | public static async Task SoundClassifierAsync(string modelName="SpeechCommands18w",SoundClassifierOptions? options=null) 182 | { 183 | var sc = new SoundClassifier(); 184 | JObjPtr ptr; 185 | if (options is null) 186 | ptr = await Ml5.Ml5Ptr.CallRefAsync("soundClassifier", modelName, (JSCallback)sc.OnModelLoadCallback); 187 | else 188 | ptr = await Ml5.Ml5Ptr.CallRefAsync("soundClassifier", modelName, options, (JSCallback)sc.OnModelLoadCallback); 189 | return sc.Init(ptr); 190 | } 191 | /// 192 | /// Facemesh is a machine-learning model that allows for facial landmark detection in the browser. 193 | /// It can detect multiple faces at once and provides 486 3D facial landmarks that describe the geometry of each face. 194 | /// Facemesh works best when the faces in view take up a large percentage of the image or video frame and it may struggle 195 | /// with small/distant faces. 196 | /// 197 | /// 198 | /// 199 | /// 200 | public static async Task FaceMeshAsync(object? video=null,FaceMeshOptions? options=null) 201 | { 202 | var fm = new FaceMesh(); 203 | JObjPtr ptr; 204 | if (options is null && video is null) 205 | ptr = await Ml5.Ml5Ptr.CallRefAsync("facemesh", (JSCallback)fm.OnModelLoadCallback); 206 | else if (video is null) 207 | ptr = await Ml5.Ml5Ptr.CallRefAsync("facemesh", options!, (JSCallback)fm.OnModelLoadCallback); 208 | else if (options is null) 209 | ptr = await Ml5.Ml5Ptr.CallRefAsync("facemesh", video, (JSCallback)fm.OnModelLoadCallback); 210 | else 211 | ptr = await Ml5.Ml5Ptr.CallRefAsync("facemesh", video, options, (JSCallback)fm.OnModelLoadCallback); 212 | return fm.Init(ptr); 213 | } 214 | } 215 | -------------------------------------------------------------------------------- /BlazorML5/Sound/SoundClassifier.cs: -------------------------------------------------------------------------------- 1 | using BlazorBindGen; 2 | using BlazorML5.Helpers; 3 | 4 | namespace BlazorML5.Sound; 5 | 6 | public class SoundClassifier 7 | { 8 | #nullable disable 9 | private JObjPtr _soundClassifier; 10 | 11 | internal SoundClassifier(){} 12 | internal SoundClassifier Init(JObjPtr soundClassifier) 13 | { 14 | _soundClassifier = soundClassifier; 15 | return this; 16 | } 17 | #nullable restore 18 | /// 19 | /// Classify sound from the microphone. 20 | /// 21 | /// 22 | public async Task ClassifyAsync() 23 | { 24 | await _soundClassifier.CallVoidAsync("classify",(JSCallback)OnClassifyCallback); 25 | } 26 | public delegate void OnModelLoadHandler(); 27 | public event OnModelLoadHandler? OnModelLoad; 28 | public delegate void OnClassifyHandler(string err, ClassificationResult[] result); 29 | public event OnClassifyHandler? OnClassify; 30 | internal void OnModelLoadCallback(JObjPtr[] args) 31 | { 32 | OnModelLoad?.Invoke(); 33 | } 34 | 35 | private void OnClassifyCallback(JObjPtr[] args) 36 | { 37 | if(OnClassify is null) 38 | return; 39 | string err=args[0].To(); 40 | ClassificationResult[] result=args[1].To(); 41 | OnClassify.Invoke(err,result); 42 | 43 | } 44 | } 45 | 46 | public record SoundClassifierOptions(double ProbabilityThreshold); -------------------------------------------------------------------------------- /BlazorML5/Text/Sentiment.cs: -------------------------------------------------------------------------------- 1 | using BlazorBindGen; 2 | 3 | namespace BlazorML5.Text; 4 | 5 | public class Sentiment 6 | { 7 | #nullable disable 8 | private JObjPtr _sentiment; 9 | /// 10 | /// set to true if the model is loaded and ready, false if it is not. 11 | /// 12 | public ValueTask Ready => _sentiment.PropValAsync("ready"); 13 | internal Sentiment(){} 14 | #nullable restore 15 | internal Sentiment Init(JObjPtr sentiment) 16 | { 17 | _sentiment = sentiment; 18 | return this; 19 | } 20 | /// 21 | /// Predict the sentiment of the text 22 | /// 23 | /// 24 | /// 25 | public async Task PredictAsync(string text) 26 | { 27 | return await _sentiment.CallAsync("predict", text); 28 | } 29 | 30 | public delegate void OnModelLoadedHandler(); 31 | 32 | /// 33 | /// Fires when model is loaded 34 | /// 35 | public event OnModelLoadedHandler? OnModelLoaded; 36 | 37 | internal void OnModelLoadedCallback(JObjPtr[] _) 38 | { 39 | OnModelLoaded?.Invoke(); 40 | } 41 | 42 | } 43 | 44 | public record SentimentScore(double Score); -------------------------------------------------------------------------------- /BlazorML5/_Imports.razor: -------------------------------------------------------------------------------- 1 | @using Microsoft.AspNetCore.Components.Web 2 | @using System 3 | @using System.Collections.Generic 4 | @using System.Linq 5 | @using System.Text 6 | @using System.Threading.Tasks 7 | @using BlazorBindGen 8 | @using Microsoft.JSInterop 9 | -------------------------------------------------------------------------------- /BlazorML5/_imports.razor.cs: -------------------------------------------------------------------------------- 1 | global using JSCallback=System.Action; -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Shivendra Pratap Singh 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ML5-Blazor 2 | [](https://www.nuget.org/packages/BlazorML5/) 3 | [](https://www.nuget.org/packages/BlazorML5) 4 |  5 | 6 | 7 | ### An Easy Machine Learning Library for Blazor. 8 | Now supports both Blazor Server and WASM and MAUI Hybrid. 9 | 10 | ## Current Features 11 | 1. Neural Network 12 | 2. Image Classification 13 | 3. Sound Classifier 14 | 4. Object Detector (YOLO and COCOSSD based) 15 | 5. PoseNet 16 | 6. Sentiment Analyzer 17 | 7. FaceMesh 18 | 19 | #### Hosted Sample 20 | [Live Demo](https://blazor-ml5-sample.netlify.com/) 21 | 22 | 23 | #### Documentation 24 | Install Asp.Net Core payload and then follow [Installation Instructions here](https://github.com/sps014/BlazorML5/wiki/BlazorML5-Installation) to configure ML5 to use it from C# Blazor app. 25 | API documentation can be followed from [here](https://learn.ml5js.org/#/reference/index) after lib configuration. 26 | . 27 | 28 | #### Sample Neural Network 29 | ```cs 30 | @page "/nn" 31 | @using BlazorML5 32 | @using BlazorML5.Helpers 33 | @inject IJSRuntime runtime 34 | 35 | Index 36 | Add Data and Train 37 | @code 38 | { 39 | NeuralNetwork _network; 40 | protected override async Task OnInitializedAsync() 41 | { 42 | await Ml5.InitAsync(runtime); 43 | 44 | _network = await Ml5.NeuralNetworkAsync(new NeuralNetworkOptions() 45 | { 46 | Task = TaskType.Classification, 47 | DataUrl = "https://raw.githubusercontent.com/ml5js/ml5-library/main/examples/p5js/NeuralNetwork/NeuralNetwork_color_classifier/data/colorData_small.json", 48 | Debug = true, 49 | Inputs = new object[]{"r", "g", "b"}, 50 | Outputs = new object[]{"label"} 51 | }); 52 | _network.OnTraining+=(l,e)=> 53 | { 54 | Console.WriteLine($"Training: {e}%"); 55 | }; 56 | _network.OnTrainingComplete+=async ()=> 57 | { 58 | Console.WriteLine($"Training Complete"); 59 | await _network.ClassifyMultipleAsync(new object[]{new object[]{12,13,14},new object[]{15,16,17}}); 60 | }; 61 | _network.OnDataLoaded+=(e)=> 62 | { 63 | Console.WriteLine($"Data Loaded"); 64 | }; 65 | _network.OnClassify+=async (l,e)=> 66 | { 67 | Console.WriteLine(e.Length); 68 | Console.WriteLine(e[0].Label); 69 | }; 70 | _network.OnClassifyMultiple+=async (l,e)=> 71 | { 72 | Console.WriteLine(e.Length); 73 | Console.WriteLine(e[0].Length); 74 | Console.WriteLine(e[0][0].Label); 75 | }; 76 | 77 | } 78 | async void AddData() 79 | { 80 | //data fetched from .csv file no need to manually add and hence directly training 81 | //await _network.NormalizeDataAsync(); 82 | await _network.TrainAsync(); 83 | } 84 | 85 | } 86 | ``` 87 | 88 | More samples [here](https://github.com/sps014/BlazorML5/tree/master/SampleApp/Pages) 89 | -------------------------------------------------------------------------------- /SampleApp/App.razor: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Not found 8 | 9 | Sorry, there's nothing at this address. 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /SampleApp/Pages/FaceMeshPage.razor: -------------------------------------------------------------------------------- 1 | @page "/facemesh" 2 | @using BlazorML5 3 | @using BlazorML5.Helpers 4 | @using BlazorML5.Image 5 | @using BlazorML5.Text 6 | @inject IJSRuntime runtime 7 | 8 | FaceMesh 9 | 12 | 13 | 14 | @code 15 | { 16 | ElementReference video; 17 | protected override async Task OnInitializedAsync() 18 | { 19 | await Ml5.InitAsync(runtime); 20 | var s = await Ml5.FaceMeshAsync(); 21 | s.OnModelLoad +=async () => { 22 | Console.WriteLine("Model Loaded"); 23 | await s.PredictAsync(video); 24 | 25 | }; 26 | s.OnPredict += async (r) => 27 | { 28 | if (r.Length > 0) 29 | Console.WriteLine(r[0].FaceInViewConfidence); 30 | await Task.Delay(500); 31 | await s.PredictAsync(video); 32 | 33 | }; 34 | } 35 | } -------------------------------------------------------------------------------- /SampleApp/Pages/FeatureExtractorPage.razor: -------------------------------------------------------------------------------- 1 | @page "/fa" 2 | @using BlazorML5 3 | @using BlazorML5.Helpers 4 | @using BlazorML5.Text 5 | @inject IJSRuntime runtime 6 | 7 | Feature Extractor 8 | 11 | 12 | @code 13 | { 14 | ElementReference video; 15 | 16 | protected override async Task OnInitializedAsync() 17 | { 18 | await Ml5.InitAsync(runtime); 19 | var feature =await Ml5.FeatureExtractorAsync(); 20 | feature.OnModelLoaded += async () => 21 | { 22 | Console.WriteLine("Model Loaded"); 23 | var r=await feature.RegressionAsync(video); 24 | 25 | r.OnVideoLoaded+=async ()=> 26 | { 27 | for(int i=0;i<100;i++) 28 | { 29 | await r.AddImageAsync(i); 30 | Console.WriteLine($"{i}/100"); 31 | } 32 | await r.TrainAsync(); 33 | }; 34 | r.OnTraining+=(loss)=> 35 | { 36 | Console.WriteLine("loss:"+loss); 37 | }; 38 | r.OnPredict += (res) => 39 | { 40 | Console.WriteLine(res.Value); 41 | }; 42 | 43 | r.OnTrainingFinished+=async ( )=> 44 | { 45 | Console.WriteLine("Training Finished"); 46 | await r.PredictAsync(video); 47 | }; 48 | 49 | }; 50 | 51 | 52 | } 53 | 54 | } -------------------------------------------------------------------------------- /SampleApp/Pages/ImageClassifierPage.razor: -------------------------------------------------------------------------------- 1 | @page "/imageclassifier" 2 | @using BlazorML5 3 | @using BlazorML5.Helpers 4 | @using BlazorML5.Text 5 | @inject IJSRuntime runtime 6 | 7 | Image Classifer 8 | Image Classifier 9 | 10 | @if (result is not null) 11 | { 12 | @result.Label wtith confidence of @result.Confidence 13 | } 14 | else if(modelLoaded) 15 | { 16 | 17 | Fetching Image Classfier Model 18 | 19 | } 20 | else 21 | { 22 | 23 | Initiaing model 24 | 25 | } 26 | 27 | 28 | 31 | 32 | @code 33 | { 34 | ElementReference video; 35 | ClassificationResult? result; 36 | bool modelLoaded; 37 | protected override async Task OnInitializedAsync() 38 | { 39 | await Ml5.InitAsync(runtime); 40 | var ic= await Ml5.ImageClassifierAsync(); 41 | ic.OnModelLoad+= async () => 42 | { 43 | Console.WriteLine("Model loaded"); 44 | modelLoaded = true; 45 | await ic.ClassifyAsync(video); 46 | }; 47 | 48 | ic.OnClassify += (s, e) => 49 | { 50 | if (e.Length > 0) 51 | { 52 | result = e[0]; 53 | StateHasChanged(); 54 | } 55 | }; 56 | 57 | } 58 | 59 | } -------------------------------------------------------------------------------- /SampleApp/Pages/Index.razor: -------------------------------------------------------------------------------- 1 | @page "/" 2 | home -------------------------------------------------------------------------------- /SampleApp/Pages/KnnClassifier.razor: -------------------------------------------------------------------------------- 1 | @page "/knn" 2 | @using BlazorML5 3 | @using BlazorML5.Helpers 4 | @using BlazorML5.Text 5 | @inject IJSRuntime runtime 6 | 7 | KNN Classifier 8 | 11 | 12 | @code 13 | { 14 | ElementReference video; 15 | 16 | protected override async Task OnInitializedAsync() 17 | { 18 | await Ml5.InitAsync(runtime); 19 | var ex = await Ml5.FeatureExtractorAsync(); 20 | ex.OnModelLoaded += async () => 21 | { 22 | var logits=await ex.InferAsync(video); 23 | var knn = await Ml5.KnnClassifierAsync(); 24 | knn.OnClassify += (e) => 25 | { 26 | Console.WriteLine(string.Join(",",e.Confidences.Keys)); 27 | Console.WriteLine(e.Label); 28 | }; 29 | for (int i = 0; i < 50; i++) 30 | { 31 | Console.WriteLine($"{i}/50"); 32 | await knn.AddExampleAsync(logits,i>25?"good":"bad"); 33 | } 34 | await knn.ClassifyAsync(logits); 35 | 36 | 37 | }; 38 | 39 | } 40 | 41 | } -------------------------------------------------------------------------------- /SampleApp/Pages/NeuralNetPage.razor: -------------------------------------------------------------------------------- 1 | @page "/nn" 2 | @using BlazorML5 3 | @using BlazorML5.Helpers 4 | @using System.Text.Json.Serialization 5 | @inject IJSRuntime runtime 6 | @inject HttpClient Http 7 | 8 | Neural Network 9 | 10 | @if (!IsDatasetLoaded) 11 | { 12 | 13 | 14 | loading and training on Dataset please wait for some time 15 | 16 | 17 | 18 | } 19 | else 20 | { 21 | 22 | Titanic Dataset Example 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | Predict 37 | 38 | 39 | 40 | @if(result is not null || error is not null) 41 | { 42 | 43 | @if(IsError) 44 | { 45 | @error 46 | } 47 | else if(result is not null) 48 | { 49 | Person : @result 50 | } 51 | 52 | } 53 | 54 | 55 | 56 | } 57 | 58 | 59 | 60 | @code 61 | { 62 | #nullable disable 63 | 64 | private bool IsDatasetLoaded = false, IsError=false; 65 | 66 | private NeuralNetwork _neuralNetwork; 67 | 68 | private string _gender, _fareClass; 69 | private int _age=60, _fare=1000; 70 | 71 | private string result,error; 72 | protected override async Task OnInitializedAsync() 73 | { 74 | await Ml5.InitAsync(runtime); 75 | NeuralNetworkOptions options = new NeuralNetworkOptions() 76 | { 77 | Inputs = 4, 78 | Outputs = 1, 79 | Task = TaskType.Classification, 80 | Debug = true 81 | }; 82 | _neuralNetwork = await Ml5.NeuralNetworkAsync(options); 83 | _neuralNetwork.OnTrainingComplete += TrainingComplete; 84 | _neuralNetwork.OnClassify += OnClassification; 85 | await LoadDataAsync(); 86 | await StartTrainAsync(); 87 | } 88 | private async Task LoadDataAsync() 89 | { 90 | var dataset = await Http.GetFromJsonAsync("data/titanic.json"); 91 | foreach (var data in dataset) 92 | { 93 | await _neuralNetwork 94 | .AddDataAsync( 95 | new object[] 96 | { 97 | data.Age, data.Fare, data.FareClass, data.Sex 98 | } 99 | , new object[] { data.Survived }); 100 | } 101 | await _neuralNetwork.NormalizeDataAsync(); 102 | 103 | Console.WriteLine("Added Dataset of size : " + dataset.Length); 104 | 105 | 106 | } 107 | private Task StartTrainAsync() 108 | { 109 | NeuralNetworkTrainOptions options = new() 110 | { 111 | Epochs = 10, 112 | BatchSize = 32 113 | }; 114 | return _neuralNetwork.TrainAsync(options); 115 | } 116 | 117 | private void TrainingComplete() 118 | { 119 | IsDatasetLoaded = true; 120 | StateHasChanged(); 121 | } 122 | private Task ClassifyAsync(MouseEventArgs _) 123 | { 124 | _gender ??= "male"; 125 | _fareClass ??= "first"; 126 | StateHasChanged(); 127 | return _neuralNetwork.ClassifyAsync(new object[] { _age, _fare, _fareClass, _gender }); 128 | } 129 | 130 | private void OnClassification(string error,ClassificationResult[] results) 131 | { 132 | result = results[0].Label; 133 | if (IsError=(!string.IsNullOrWhiteSpace(error))) 134 | this.error = error; 135 | StateHasChanged(); 136 | } 137 | class Data 138 | { 139 | [JsonPropertyName("survived")] 140 | public string Survived { get; set; } 141 | [JsonPropertyName("fare_class")] 142 | public string FareClass { get; set; } 143 | [JsonPropertyName("sex")] 144 | public string Sex { get; set; } 145 | [JsonPropertyName("age")] 146 | public int Age { get; set; } 147 | [JsonPropertyName("fare")] 148 | public int Fare { get; set; } 149 | } 150 | #nullable restore 151 | } -------------------------------------------------------------------------------- /SampleApp/Pages/NnPage.razor: -------------------------------------------------------------------------------- 1 | @page "/nkn" 2 | @using BlazorML5 3 | @using BlazorML5.Helpers 4 | @inject IJSRuntime runtime 5 | 6 | Neural Network 7 | Add Data and Train 8 | @code 9 | { 10 | NeuralNetwork _network; 11 | protected override async Task OnInitializedAsync() 12 | { 13 | await Ml5.InitAsync(runtime); 14 | 15 | _network = await Ml5.NeuralNetworkAsync(new NeuralNetworkOptions() 16 | { 17 | Task = TaskType.Classification, 18 | DataUrl = "https://raw.githubusercontent.com/ml5js/ml5-library/main/examples/p5js/NeuralNetwork/NeuralNetwork_color_classifier/data/colorData_small.json", 19 | Debug = true, 20 | Inputs = new object[]{"r", "g", "b"}, 21 | Outputs = new object[]{"label"} 22 | }); 23 | _network.OnTraining+=(l,e)=> 24 | { 25 | Console.WriteLine($"Training: {e}%"); 26 | }; 27 | _network.OnTrainingComplete+=async ()=> 28 | { 29 | Console.WriteLine($"Training Complete"); 30 | await _network.ClassifyMultipleAsync(new object[]{new object[]{12,13,14},new object[]{15,16,17}}); 31 | }; 32 | _network.OnDataLoaded+=(e)=> 33 | { 34 | Console.WriteLine($"Data Loaded"); 35 | }; 36 | _network.OnClassify+=async (l,e)=> 37 | { 38 | Console.WriteLine(e.Length); 39 | Console.WriteLine(e[0].Label); 40 | }; 41 | _network.OnClassifyMultiple+=async (l,e)=> 42 | { 43 | Console.WriteLine(e.Length); 44 | Console.WriteLine(e[0].Length); 45 | Console.WriteLine(e[0][0].Label); 46 | }; 47 | 48 | } 49 | async void AddData() 50 | { 51 | //data feteched from .csv file no need to manually add 52 | //await _network.AddDataAsync(new object[]{ 1},new object[]{1 }); 53 | //await _network.AddDataAsync(new object[]{ 2},new object[]{4 }); 54 | //await _network.AddDataAsync(new object[]{ 3},new object[]{6 }); 55 | //await _network.AddDataAsync(new object[]{ 3},new object[]{6 }); 56 | //await _network.AddDataAsync(new object[]{ 3},new object[]{6 }); 57 | //await _network.AddDataAsync(new object[]{ 3},new object[]{6 }); 58 | //await _network.NormalizeDataAsync(); 59 | await _network.TrainAsync(); 60 | } 61 | 62 | } -------------------------------------------------------------------------------- /SampleApp/Pages/ObjectDetectorPage.razor: -------------------------------------------------------------------------------- 1 | @page "/object" 2 | @using BlazorML5 3 | @using BlazorML5.Helpers 4 | @using BlazorML5.Image 5 | @using BlazorML5.Text 6 | @inject IJSRuntime runtime 7 | 8 | ObjectDetector 9 | 12 | 13 | @code 14 | { 15 | ElementReference video; 16 | ObjectDetector p; 17 | protected override async Task OnInitializedAsync() 18 | { 19 | await Ml5.InitAsync(runtime); 20 | p = await Ml5.ObjectDetectorAsync(); 21 | p.OnDetect += (e, r) => 22 | { 23 | Console.WriteLine(r[0].Label); 24 | }; 25 | p.OnModelLoad += async () => 26 | { 27 | Console.WriteLine("Model loaded"); 28 | await p.DetectAsync((video)); 29 | }; 30 | 31 | 32 | 33 | 34 | } 35 | 36 | } -------------------------------------------------------------------------------- /SampleApp/Pages/PosenetPage.razor: -------------------------------------------------------------------------------- 1 | @page "/posenet" 2 | @using BlazorML5 3 | @using BlazorML5.Helpers 4 | @using BlazorML5.Image 5 | @using BlazorML5.Text 6 | @inject IJSRuntime runtime 7 | 8 | PoseNet 9 | 12 | 13 | @code 14 | { 15 | ElementReference video; 16 | PoseNet p; 17 | protected override async Task OnInitializedAsync() 18 | { 19 | await Ml5.InitAsync(runtime); 20 | p = await Ml5.PoseNetAsync(video); 21 | p.OnModelLoad += async () => 22 | { 23 | Console.WriteLine("Model loaded"); 24 | await p.SinglePoseAsync(video); 25 | }; 26 | p.OnPose+= async (e) => 27 | { 28 | if (e.Count>0) 29 | Console.WriteLine(e[0].Pose.Score); 30 | Console.WriteLine(e[0].Pose.LeftAnkle.Confidence); 31 | 32 | }; 33 | 34 | } 35 | 36 | async void Call() 37 | { 38 | Console.WriteLine("Calling"); 39 | } 40 | 41 | } -------------------------------------------------------------------------------- /SampleApp/Pages/SentimentPage.razor: -------------------------------------------------------------------------------- 1 | @page "/sa" 2 | @using BlazorML5 3 | @using BlazorML5.Text 4 | @inject IJSRuntime runtime 5 | 6 | Sentiment 7 | @code 8 | { 9 | protected override async Task OnInitializedAsync() 10 | { 11 | await Ml5.InitAsync(runtime); 12 | 13 | var _sentiment =await Ml5.SentimentAsync(); 14 | _sentiment.OnModelLoaded += async () => 15 | { 16 | Console.WriteLine("Model Loaded"); 17 | Console.WriteLine((await _sentiment.PredictAsync("here is some text")).Score); 18 | }; 19 | 20 | } 21 | 22 | } -------------------------------------------------------------------------------- /SampleApp/Pages/SoundClassifer.razor: -------------------------------------------------------------------------------- 1 | @page "/sound" 2 | @using BlazorML5 3 | @using BlazorML5.Helpers 4 | @using BlazorML5.Image 5 | @using BlazorML5.Text 6 | @inject IJSRuntime runtime 7 | 8 | Sound Classifier 9 | 12 | 13 | 14 | @code 15 | { 16 | ElementReference video; 17 | protected override async Task OnInitializedAsync() 18 | { 19 | await Ml5.InitAsync(runtime); 20 | var s = await Ml5.SoundClassifierAsync(); 21 | s.OnModelLoad +=async () => { 22 | Console.WriteLine("Model Loaded"); 23 | await s.ClassifyAsync(); 24 | 25 | }; 26 | s.OnClassify += (e, r) => 27 | { 28 | if (r.Length > 0) 29 | Console.WriteLine(r[0].Label); 30 | }; 31 | } 32 | 33 | } -------------------------------------------------------------------------------- /SampleApp/Program.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Components.Web; 2 | using Microsoft.AspNetCore.Components.WebAssembly.Hosting; 3 | using MudBlazor.Services; 4 | using SampleApp; 5 | 6 | var builder = WebAssemblyHostBuilder.CreateDefault(args); 7 | builder.RootComponents.Add("#app"); 8 | builder.RootComponents.Add("head::after"); 9 | 10 | builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) }); 11 | builder.Services.AddMudServices(); 12 | 13 | await builder.Build().RunAsync(); 14 | -------------------------------------------------------------------------------- /SampleApp/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "windowsAuthentication": false, 4 | "anonymousAuthentication": true, 5 | "iisExpress": { 6 | "applicationUrl": "http://localhost:59885", 7 | "sslPort": 44390 8 | } 9 | }, 10 | "profiles": { 11 | "SampleApp": { 12 | "commandName": "Project", 13 | "dotnetRunMessages": true, 14 | "launchBrowser": true, 15 | "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", 16 | "applicationUrl": "https://localhost:7273;http://localhost:5273", 17 | "environmentVariables": { 18 | "ASPNETCORE_ENVIRONMENT": "Development" 19 | } 20 | }, 21 | "IIS Express": { 22 | "commandName": "IISExpress", 23 | "launchBrowser": true, 24 | "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", 25 | "environmentVariables": { 26 | "ASPNETCORE_ENVIRONMENT": "Development" 27 | } 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /SampleApp/SampleApp.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net6.0 5 | enable 6 | enable 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /SampleApp/SampleApp.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.5.002.0 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SampleApp", "SampleApp.csproj", "{2C86B4EE-3F3E-482C-BDD7-810C8E3E632A}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {2C86B4EE-3F3E-482C-BDD7-810C8E3E632A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {2C86B4EE-3F3E-482C-BDD7-810C8E3E632A}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {2C86B4EE-3F3E-482C-BDD7-810C8E3E632A}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {2C86B4EE-3F3E-482C-BDD7-810C8E3E632A}.Release|Any CPU.Build.0 = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | GlobalSection(ExtensibilityGlobals) = postSolution 23 | SolutionGuid = {DC575F90-B536-491F-A402-1A0F4E193929} 24 | EndGlobalSection 25 | EndGlobal 26 | -------------------------------------------------------------------------------- /SampleApp/Shared/MainLayout.razor: -------------------------------------------------------------------------------- 1 | @inherits LayoutComponentBase 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | BlazorML5 Sample 12 | 13 | 14 | 15 | 16 | 17 | @Body 18 | 19 | 20 | 21 | 22 | @code 23 | { 24 | bool _drawerOpen = false; 25 | void DrawerToggle() 26 | { 27 | _drawerOpen = !_drawerOpen; 28 | } 29 | } -------------------------------------------------------------------------------- /SampleApp/Shared/MainLayout.razor.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sps014/BlazorML5/53a250024eead2168d2af91ad07c4307cab3a14b/SampleApp/Shared/MainLayout.razor.css -------------------------------------------------------------------------------- /SampleApp/Shared/NavMenu.razor: -------------------------------------------------------------------------------- 1 | 2 | Home 3 | Neural Network 4 | KNN 5 | Posenet 6 | Face Mesh 7 | Object Detector 8 | Sound Classifier 9 | Image Classifier 10 | Feature Extractor 11 | Sentiment Analysis 12 | 13 | @*Login Page*@ 14 | -------------------------------------------------------------------------------- /SampleApp/Shared/NavMenu.razor.css: -------------------------------------------------------------------------------- 1 | .navbar-toggler { 2 | background-color: rgba(255, 255, 255, 0.1); 3 | } 4 | 5 | .top-row { 6 | height: 3.5rem; 7 | background-color: rgba(0,0,0,0.4); 8 | } 9 | 10 | .navbar-brand { 11 | font-size: 1.1rem; 12 | } 13 | 14 | .oi { 15 | width: 2rem; 16 | font-size: 1.1rem; 17 | vertical-align: text-top; 18 | top: -2px; 19 | } 20 | 21 | .nav-item { 22 | font-size: 0.9rem; 23 | padding-bottom: 0.5rem; 24 | } 25 | 26 | .nav-item:first-of-type { 27 | padding-top: 1rem; 28 | } 29 | 30 | .nav-item:last-of-type { 31 | padding-bottom: 1rem; 32 | } 33 | 34 | .nav-item ::deep a { 35 | color: #d7d7d7; 36 | border-radius: 4px; 37 | height: 3rem; 38 | display: flex; 39 | align-items: center; 40 | line-height: 3rem; 41 | } 42 | 43 | .nav-item ::deep a.active { 44 | background-color: rgba(255,255,255,0.25); 45 | color: white; 46 | } 47 | 48 | .nav-item ::deep a:hover { 49 | background-color: rgba(255,255,255,0.1); 50 | color: white; 51 | } 52 | 53 | @media (min-width: 641px) { 54 | .navbar-toggler { 55 | display: none; 56 | } 57 | 58 | .collapse { 59 | /* Never collapse the sidebar for wide screens */ 60 | display: block; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /SampleApp/_Imports.razor: -------------------------------------------------------------------------------- 1 | @using System.Net.Http 2 | @using System.Net.Http.Json 3 | @using Microsoft.AspNetCore.Components.Forms 4 | @using Microsoft.AspNetCore.Components.Routing 5 | @using Microsoft.AspNetCore.Components.Web 6 | @using Microsoft.AspNetCore.Components.Web.Virtualization 7 | @using Microsoft.AspNetCore.Components.WebAssembly.Http 8 | @using Microsoft.JSInterop 9 | @using SampleApp 10 | @using SampleApp.Shared 11 | @using MudBlazor 12 | -------------------------------------------------------------------------------- /SampleApp/wwwroot/css/app.css: -------------------------------------------------------------------------------- 1 | @import url('open-iconic/font/css/open-iconic-bootstrap.min.css'); 2 | 3 | 4 | .valid.modified:not([type=checkbox]) { 5 | outline: 1px solid #26b050; 6 | } 7 | 8 | .invalid { 9 | outline: 1px solid red; 10 | } 11 | 12 | .validation-message { 13 | color: red; 14 | } 15 | 16 | #blazor-error-ui { 17 | background: lightyellow; 18 | bottom: 0; 19 | box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2); 20 | display: none; 21 | left: 0; 22 | padding: 0.6rem 1.25rem 0.7rem 1.25rem; 23 | position: fixed; 24 | width: 100%; 25 | z-index: 1000; 26 | } 27 | 28 | #blazor-error-ui .dismiss { 29 | cursor: pointer; 30 | position: absolute; 31 | right: 0.75rem; 32 | top: 0.5rem; 33 | } 34 | 35 | .blazor-error-boundary { 36 | background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDggNjYuMDE4MyAyNjMuNTg2IDY2LjAxODNaTTI2My41NzYgODYuMDU0N0MyNjEuMDQ5IDg2LjA1NDcgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat 1rem/1.8rem, #b32121; 37 | padding: 1rem 1rem 1rem 3.7rem; 38 | color: white; 39 | } 40 | 41 | .blazor-error-boundary::after { 42 | content: "An error has occurred." 43 | } 44 | -------------------------------------------------------------------------------- /SampleApp/wwwroot/css/open-iconic/FONT-LICENSE: -------------------------------------------------------------------------------- 1 | SIL OPEN FONT LICENSE Version 1.1 2 | 3 | Copyright (c) 2014 Waybury 4 | 5 | PREAMBLE 6 | The goals of the Open Font License (OFL) are to stimulate worldwide 7 | development of collaborative font projects, to support the font creation 8 | efforts of academic and linguistic communities, and to provide a free and 9 | open framework in which fonts may be shared and improved in partnership 10 | with others. 11 | 12 | The OFL allows the licensed fonts to be used, studied, modified and 13 | redistributed freely as long as they are not sold by themselves. The 14 | fonts, including any derivative works, can be bundled, embedded, 15 | redistributed and/or sold with any software provided that any reserved 16 | names are not used by derivative works. The fonts and derivatives, 17 | however, cannot be released under any other type of license. The 18 | requirement for fonts to remain under this license does not apply 19 | to any document created using the fonts or their derivatives. 20 | 21 | DEFINITIONS 22 | "Font Software" refers to the set of files released by the Copyright 23 | Holder(s) under this license and clearly marked as such. This may 24 | include source files, build scripts and documentation. 25 | 26 | "Reserved Font Name" refers to any names specified as such after the 27 | copyright statement(s). 28 | 29 | "Original Version" refers to the collection of Font Software components as 30 | distributed by the Copyright Holder(s). 31 | 32 | "Modified Version" refers to any derivative made by adding to, deleting, 33 | or substituting -- in part or in whole -- any of the components of the 34 | Original Version, by changing formats or by porting the Font Software to a 35 | new environment. 36 | 37 | "Author" refers to any designer, engineer, programmer, technical 38 | writer or other person who contributed to the Font Software. 39 | 40 | PERMISSION & CONDITIONS 41 | Permission is hereby granted, free of charge, to any person obtaining 42 | a copy of the Font Software, to use, study, copy, merge, embed, modify, 43 | redistribute, and sell modified and unmodified copies of the Font 44 | Software, subject to the following conditions: 45 | 46 | 1) Neither the Font Software nor any of its individual components, 47 | in Original or Modified Versions, may be sold by itself. 48 | 49 | 2) Original or Modified Versions of the Font Software may be bundled, 50 | redistributed and/or sold with any software, provided that each copy 51 | contains the above copyright notice and this license. These can be 52 | included either as stand-alone text files, human-readable headers or 53 | in the appropriate machine-readable metadata fields within text or 54 | binary files as long as those fields can be easily viewed by the user. 55 | 56 | 3) No Modified Version of the Font Software may use the Reserved Font 57 | Name(s) unless explicit written permission is granted by the corresponding 58 | Copyright Holder. This restriction only applies to the primary font name as 59 | presented to the users. 60 | 61 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font 62 | Software shall not be used to promote, endorse or advertise any 63 | Modified Version, except to acknowledge the contribution(s) of the 64 | Copyright Holder(s) and the Author(s) or with their explicit written 65 | permission. 66 | 67 | 5) The Font Software, modified or unmodified, in part or in whole, 68 | must be distributed entirely under this license, and must not be 69 | distributed under any other license. The requirement for fonts to 70 | remain under this license does not apply to any document created 71 | using the Font Software. 72 | 73 | TERMINATION 74 | This license becomes null and void if any of the above conditions are 75 | not met. 76 | 77 | DISCLAIMER 78 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 79 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF 80 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT 81 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE 82 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 83 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 84 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 85 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM 86 | OTHER DEALINGS IN THE FONT SOFTWARE. 87 | -------------------------------------------------------------------------------- /SampleApp/wwwroot/css/open-iconic/ICON-LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Waybury 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /SampleApp/wwwroot/css/open-iconic/README.md: -------------------------------------------------------------------------------- 1 | [Open Iconic v1.1.1](http://useiconic.com/open) 2 | =========== 3 | 4 | ### Open Iconic is the open source sibling of [Iconic](http://useiconic.com). It is a hyper-legible collection of 223 icons with a tiny footprint—ready to use with Bootstrap and Foundation. [View the collection](http://useiconic.com/open#icons) 5 | 6 | 7 | 8 | ## What's in Open Iconic? 9 | 10 | * 223 icons designed to be legible down to 8 pixels 11 | * Super-light SVG files - 61.8 for the entire set 12 | * SVG sprite—the modern replacement for icon fonts 13 | * Webfont (EOT, OTF, SVG, TTF, WOFF), PNG and WebP formats 14 | * Webfont stylesheets (including versions for Bootstrap and Foundation) in CSS, LESS, SCSS and Stylus formats 15 | * PNG and WebP raster images in 8px, 16px, 24px, 32px, 48px and 64px. 16 | 17 | 18 | ## Getting Started 19 | 20 | #### For code samples and everything else you need to get started with Open Iconic, check out our [Icons](http://useiconic.com/open#icons) and [Reference](http://useiconic.com/open#reference) sections. 21 | 22 | ### General Usage 23 | 24 | #### Using Open Iconic's SVGs 25 | 26 | We like SVGs and we think they're the way to display icons on the web. Since Open Iconic are just basic SVGs, we suggest you display them like you would any other image (don't forget the `alt` attribute). 27 | 28 | ``` 29 | 30 | ``` 31 | 32 | #### Using Open Iconic's SVG Sprite 33 | 34 | Open Iconic also comes in a SVG sprite which allows you to display all the icons in the set with a single request. It's like an icon font, without being a hack. 35 | 36 | Adding an icon from an SVG sprite is a little different than what you're used to, but it's still a piece of cake. *Tip: To make your icons easily style able, we suggest adding a general class to the* `` *tag and a unique class name for each different icon in the* `` *tag.* 37 | 38 | ``` 39 | 40 | 41 | 42 | ``` 43 | 44 | Sizing icons only needs basic CSS. All the icons are in a square format, so just set the `` tag with equal width and height dimensions. 45 | 46 | ``` 47 | .icon { 48 | width: 16px; 49 | height: 16px; 50 | } 51 | ``` 52 | 53 | Coloring icons is even easier. All you need to do is set the `fill` rule on the `` tag. 54 | 55 | ``` 56 | .icon-account-login { 57 | fill: #f00; 58 | } 59 | ``` 60 | 61 | To learn more about SVG Sprites, read [Chris Coyier's guide](http://css-tricks.com/svg-sprites-use-better-icon-fonts/). 62 | 63 | #### Using Open Iconic's Icon Font... 64 | 65 | 66 | ##### …with Bootstrap 67 | 68 | You can find our Bootstrap stylesheets in `font/css/open-iconic-bootstrap.{css, less, scss, styl}` 69 | 70 | 71 | ``` 72 | 73 | ``` 74 | 75 | 76 | ``` 77 | 78 | ``` 79 | 80 | ##### …with Foundation 81 | 82 | You can find our Foundation stylesheets in `font/css/open-iconic-foundation.{css, less, scss, styl}` 83 | 84 | ``` 85 | 86 | ``` 87 | 88 | 89 | ``` 90 | 91 | ``` 92 | 93 | ##### …on its own 94 | 95 | You can find our default stylesheets in `font/css/open-iconic.{css, less, scss, styl}` 96 | 97 | ``` 98 | 99 | ``` 100 | 101 | ``` 102 | 103 | ``` 104 | 105 | 106 | ## License 107 | 108 | ### Icons 109 | 110 | All code (including SVG markup) is under the [MIT License](http://opensource.org/licenses/MIT). 111 | 112 | ### Fonts 113 | 114 | All fonts are under the [SIL Licensed](http://scripts.sil.org/cms/scripts/page.php?item_id=OFL_web). 115 | -------------------------------------------------------------------------------- /SampleApp/wwwroot/css/open-iconic/font/css/open-iconic-bootstrap.min.css: -------------------------------------------------------------------------------- 1 | @font-face{font-family:Icons;src:url(../fonts/open-iconic.eot);src:url(../fonts/open-iconic.eot?#iconic-sm) format('embedded-opentype'),url(../fonts/open-iconic.woff) format('woff'),url(../fonts/open-iconic.ttf) format('truetype'),url(../fonts/open-iconic.otf) format('opentype'),url(../fonts/open-iconic.svg#iconic-sm) format('svg');font-weight:400;font-style:normal}.oi{position:relative;top:1px;display:inline-block;speak:none;font-family:Icons;font-style:normal;font-weight:400;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.oi:empty:before{width:1em;text-align:center;box-sizing:content-box}.oi.oi-align-center:before{text-align:center}.oi.oi-align-left:before{text-align:left}.oi.oi-align-right:before{text-align:right}.oi.oi-flip-horizontal:before{-webkit-transform:scale(-1,1);-ms-transform:scale(-1,1);transform:scale(-1,1)}.oi.oi-flip-vertical:before{-webkit-transform:scale(1,-1);-ms-transform:scale(-1,1);transform:scale(1,-1)}.oi.oi-flip-horizontal-vertical:before{-webkit-transform:scale(-1,-1);-ms-transform:scale(-1,1);transform:scale(-1,-1)}.oi-account-login:before{content:'\e000'}.oi-account-logout:before{content:'\e001'}.oi-action-redo:before{content:'\e002'}.oi-action-undo:before{content:'\e003'}.oi-align-center:before{content:'\e004'}.oi-align-left:before{content:'\e005'}.oi-align-right:before{content:'\e006'}.oi-aperture:before{content:'\e007'}.oi-arrow-bottom:before{content:'\e008'}.oi-arrow-circle-bottom:before{content:'\e009'}.oi-arrow-circle-left:before{content:'\e00a'}.oi-arrow-circle-right:before{content:'\e00b'}.oi-arrow-circle-top:before{content:'\e00c'}.oi-arrow-left:before{content:'\e00d'}.oi-arrow-right:before{content:'\e00e'}.oi-arrow-thick-bottom:before{content:'\e00f'}.oi-arrow-thick-left:before{content:'\e010'}.oi-arrow-thick-right:before{content:'\e011'}.oi-arrow-thick-top:before{content:'\e012'}.oi-arrow-top:before{content:'\e013'}.oi-audio-spectrum:before{content:'\e014'}.oi-audio:before{content:'\e015'}.oi-badge:before{content:'\e016'}.oi-ban:before{content:'\e017'}.oi-bar-chart:before{content:'\e018'}.oi-basket:before{content:'\e019'}.oi-battery-empty:before{content:'\e01a'}.oi-battery-full:before{content:'\e01b'}.oi-beaker:before{content:'\e01c'}.oi-bell:before{content:'\e01d'}.oi-bluetooth:before{content:'\e01e'}.oi-bold:before{content:'\e01f'}.oi-bolt:before{content:'\e020'}.oi-book:before{content:'\e021'}.oi-bookmark:before{content:'\e022'}.oi-box:before{content:'\e023'}.oi-briefcase:before{content:'\e024'}.oi-british-pound:before{content:'\e025'}.oi-browser:before{content:'\e026'}.oi-brush:before{content:'\e027'}.oi-bug:before{content:'\e028'}.oi-bullhorn:before{content:'\e029'}.oi-calculator:before{content:'\e02a'}.oi-calendar:before{content:'\e02b'}.oi-camera-slr:before{content:'\e02c'}.oi-caret-bottom:before{content:'\e02d'}.oi-caret-left:before{content:'\e02e'}.oi-caret-right:before{content:'\e02f'}.oi-caret-top:before{content:'\e030'}.oi-cart:before{content:'\e031'}.oi-chat:before{content:'\e032'}.oi-check:before{content:'\e033'}.oi-chevron-bottom:before{content:'\e034'}.oi-chevron-left:before{content:'\e035'}.oi-chevron-right:before{content:'\e036'}.oi-chevron-top:before{content:'\e037'}.oi-circle-check:before{content:'\e038'}.oi-circle-x:before{content:'\e039'}.oi-clipboard:before{content:'\e03a'}.oi-clock:before{content:'\e03b'}.oi-cloud-download:before{content:'\e03c'}.oi-cloud-upload:before{content:'\e03d'}.oi-cloud:before{content:'\e03e'}.oi-cloudy:before{content:'\e03f'}.oi-code:before{content:'\e040'}.oi-cog:before{content:'\e041'}.oi-collapse-down:before{content:'\e042'}.oi-collapse-left:before{content:'\e043'}.oi-collapse-right:before{content:'\e044'}.oi-collapse-up:before{content:'\e045'}.oi-command:before{content:'\e046'}.oi-comment-square:before{content:'\e047'}.oi-compass:before{content:'\e048'}.oi-contrast:before{content:'\e049'}.oi-copywriting:before{content:'\e04a'}.oi-credit-card:before{content:'\e04b'}.oi-crop:before{content:'\e04c'}.oi-dashboard:before{content:'\e04d'}.oi-data-transfer-download:before{content:'\e04e'}.oi-data-transfer-upload:before{content:'\e04f'}.oi-delete:before{content:'\e050'}.oi-dial:before{content:'\e051'}.oi-document:before{content:'\e052'}.oi-dollar:before{content:'\e053'}.oi-double-quote-sans-left:before{content:'\e054'}.oi-double-quote-sans-right:before{content:'\e055'}.oi-double-quote-serif-left:before{content:'\e056'}.oi-double-quote-serif-right:before{content:'\e057'}.oi-droplet:before{content:'\e058'}.oi-eject:before{content:'\e059'}.oi-elevator:before{content:'\e05a'}.oi-ellipses:before{content:'\e05b'}.oi-envelope-closed:before{content:'\e05c'}.oi-envelope-open:before{content:'\e05d'}.oi-euro:before{content:'\e05e'}.oi-excerpt:before{content:'\e05f'}.oi-expand-down:before{content:'\e060'}.oi-expand-left:before{content:'\e061'}.oi-expand-right:before{content:'\e062'}.oi-expand-up:before{content:'\e063'}.oi-external-link:before{content:'\e064'}.oi-eye:before{content:'\e065'}.oi-eyedropper:before{content:'\e066'}.oi-file:before{content:'\e067'}.oi-fire:before{content:'\e068'}.oi-flag:before{content:'\e069'}.oi-flash:before{content:'\e06a'}.oi-folder:before{content:'\e06b'}.oi-fork:before{content:'\e06c'}.oi-fullscreen-enter:before{content:'\e06d'}.oi-fullscreen-exit:before{content:'\e06e'}.oi-globe:before{content:'\e06f'}.oi-graph:before{content:'\e070'}.oi-grid-four-up:before{content:'\e071'}.oi-grid-three-up:before{content:'\e072'}.oi-grid-two-up:before{content:'\e073'}.oi-hard-drive:before{content:'\e074'}.oi-header:before{content:'\e075'}.oi-headphones:before{content:'\e076'}.oi-heart:before{content:'\e077'}.oi-home:before{content:'\e078'}.oi-image:before{content:'\e079'}.oi-inbox:before{content:'\e07a'}.oi-infinity:before{content:'\e07b'}.oi-info:before{content:'\e07c'}.oi-italic:before{content:'\e07d'}.oi-justify-center:before{content:'\e07e'}.oi-justify-left:before{content:'\e07f'}.oi-justify-right:before{content:'\e080'}.oi-key:before{content:'\e081'}.oi-laptop:before{content:'\e082'}.oi-layers:before{content:'\e083'}.oi-lightbulb:before{content:'\e084'}.oi-link-broken:before{content:'\e085'}.oi-link-intact:before{content:'\e086'}.oi-list-rich:before{content:'\e087'}.oi-list:before{content:'\e088'}.oi-location:before{content:'\e089'}.oi-lock-locked:before{content:'\e08a'}.oi-lock-unlocked:before{content:'\e08b'}.oi-loop-circular:before{content:'\e08c'}.oi-loop-square:before{content:'\e08d'}.oi-loop:before{content:'\e08e'}.oi-magnifying-glass:before{content:'\e08f'}.oi-map-marker:before{content:'\e090'}.oi-map:before{content:'\e091'}.oi-media-pause:before{content:'\e092'}.oi-media-play:before{content:'\e093'}.oi-media-record:before{content:'\e094'}.oi-media-skip-backward:before{content:'\e095'}.oi-media-skip-forward:before{content:'\e096'}.oi-media-step-backward:before{content:'\e097'}.oi-media-step-forward:before{content:'\e098'}.oi-media-stop:before{content:'\e099'}.oi-medical-cross:before{content:'\e09a'}.oi-menu:before{content:'\e09b'}.oi-microphone:before{content:'\e09c'}.oi-minus:before{content:'\e09d'}.oi-monitor:before{content:'\e09e'}.oi-moon:before{content:'\e09f'}.oi-move:before{content:'\e0a0'}.oi-musical-note:before{content:'\e0a1'}.oi-paperclip:before{content:'\e0a2'}.oi-pencil:before{content:'\e0a3'}.oi-people:before{content:'\e0a4'}.oi-person:before{content:'\e0a5'}.oi-phone:before{content:'\e0a6'}.oi-pie-chart:before{content:'\e0a7'}.oi-pin:before{content:'\e0a8'}.oi-play-circle:before{content:'\e0a9'}.oi-plus:before{content:'\e0aa'}.oi-power-standby:before{content:'\e0ab'}.oi-print:before{content:'\e0ac'}.oi-project:before{content:'\e0ad'}.oi-pulse:before{content:'\e0ae'}.oi-puzzle-piece:before{content:'\e0af'}.oi-question-mark:before{content:'\e0b0'}.oi-rain:before{content:'\e0b1'}.oi-random:before{content:'\e0b2'}.oi-reload:before{content:'\e0b3'}.oi-resize-both:before{content:'\e0b4'}.oi-resize-height:before{content:'\e0b5'}.oi-resize-width:before{content:'\e0b6'}.oi-rss-alt:before{content:'\e0b7'}.oi-rss:before{content:'\e0b8'}.oi-script:before{content:'\e0b9'}.oi-share-boxed:before{content:'\e0ba'}.oi-share:before{content:'\e0bb'}.oi-shield:before{content:'\e0bc'}.oi-signal:before{content:'\e0bd'}.oi-signpost:before{content:'\e0be'}.oi-sort-ascending:before{content:'\e0bf'}.oi-sort-descending:before{content:'\e0c0'}.oi-spreadsheet:before{content:'\e0c1'}.oi-star:before{content:'\e0c2'}.oi-sun:before{content:'\e0c3'}.oi-tablet:before{content:'\e0c4'}.oi-tag:before{content:'\e0c5'}.oi-tags:before{content:'\e0c6'}.oi-target:before{content:'\e0c7'}.oi-task:before{content:'\e0c8'}.oi-terminal:before{content:'\e0c9'}.oi-text:before{content:'\e0ca'}.oi-thumb-down:before{content:'\e0cb'}.oi-thumb-up:before{content:'\e0cc'}.oi-timer:before{content:'\e0cd'}.oi-transfer:before{content:'\e0ce'}.oi-trash:before{content:'\e0cf'}.oi-underline:before{content:'\e0d0'}.oi-vertical-align-bottom:before{content:'\e0d1'}.oi-vertical-align-center:before{content:'\e0d2'}.oi-vertical-align-top:before{content:'\e0d3'}.oi-video:before{content:'\e0d4'}.oi-volume-high:before{content:'\e0d5'}.oi-volume-low:before{content:'\e0d6'}.oi-volume-off:before{content:'\e0d7'}.oi-warning:before{content:'\e0d8'}.oi-wifi:before{content:'\e0d9'}.oi-wrench:before{content:'\e0da'}.oi-x:before{content:'\e0db'}.oi-yen:before{content:'\e0dc'}.oi-zoom-in:before{content:'\e0dd'}.oi-zoom-out:before{content:'\e0de'} -------------------------------------------------------------------------------- /SampleApp/wwwroot/css/open-iconic/font/fonts/open-iconic.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sps014/BlazorML5/53a250024eead2168d2af91ad07c4307cab3a14b/SampleApp/wwwroot/css/open-iconic/font/fonts/open-iconic.eot -------------------------------------------------------------------------------- /SampleApp/wwwroot/css/open-iconic/font/fonts/open-iconic.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sps014/BlazorML5/53a250024eead2168d2af91ad07c4307cab3a14b/SampleApp/wwwroot/css/open-iconic/font/fonts/open-iconic.otf -------------------------------------------------------------------------------- /SampleApp/wwwroot/css/open-iconic/font/fonts/open-iconic.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 8 | Created by FontForge 20120731 at Tue Jul 1 20:39:22 2014 9 | By P.J. Onori 10 | Created by P.J. Onori with FontForge 2.0 (http://fontforge.sf.net) 11 | 12 | 13 | 14 | 27 | 28 | 30 | 32 | 34 | 36 | 38 | 40 | 42 | 45 | 47 | 49 | 51 | 53 | 55 | 57 | 59 | 61 | 63 | 65 | 67 | 69 | 71 | 74 | 76 | 79 | 81 | 84 | 86 | 88 | 91 | 93 | 95 | 98 | 100 | 102 | 104 | 106 | 109 | 112 | 115 | 117 | 121 | 123 | 125 | 127 | 130 | 132 | 134 | 136 | 138 | 141 | 143 | 145 | 147 | 149 | 151 | 153 | 155 | 157 | 159 | 162 | 165 | 167 | 169 | 172 | 174 | 177 | 179 | 181 | 183 | 185 | 189 | 191 | 194 | 196 | 198 | 200 | 202 | 205 | 207 | 209 | 211 | 213 | 215 | 218 | 220 | 222 | 224 | 226 | 228 | 230 | 232 | 234 | 236 | 238 | 241 | 243 | 245 | 247 | 249 | 251 | 253 | 256 | 259 | 261 | 263 | 265 | 267 | 269 | 272 | 274 | 276 | 280 | 282 | 285 | 287 | 289 | 292 | 295 | 298 | 300 | 302 | 304 | 306 | 309 | 312 | 314 | 316 | 318 | 320 | 322 | 324 | 326 | 330 | 334 | 338 | 340 | 343 | 345 | 347 | 349 | 351 | 353 | 355 | 358 | 360 | 363 | 365 | 367 | 369 | 371 | 373 | 375 | 377 | 379 | 381 | 383 | 386 | 388 | 390 | 392 | 394 | 396 | 399 | 401 | 404 | 406 | 408 | 410 | 412 | 414 | 416 | 419 | 421 | 423 | 425 | 428 | 431 | 435 | 438 | 440 | 442 | 444 | 446 | 448 | 451 | 453 | 455 | 457 | 460 | 462 | 464 | 466 | 468 | 471 | 473 | 477 | 479 | 481 | 483 | 486 | 488 | 490 | 492 | 494 | 496 | 499 | 501 | 504 | 506 | 509 | 512 | 515 | 517 | 520 | 522 | 524 | 526 | 529 | 532 | 534 | 536 | 539 | 542 | 543 | 544 | -------------------------------------------------------------------------------- /SampleApp/wwwroot/css/open-iconic/font/fonts/open-iconic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sps014/BlazorML5/53a250024eead2168d2af91ad07c4307cab3a14b/SampleApp/wwwroot/css/open-iconic/font/fonts/open-iconic.ttf -------------------------------------------------------------------------------- /SampleApp/wwwroot/css/open-iconic/font/fonts/open-iconic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sps014/BlazorML5/53a250024eead2168d2af91ad07c4307cab3a14b/SampleApp/wwwroot/css/open-iconic/font/fonts/open-iconic.woff -------------------------------------------------------------------------------- /SampleApp/wwwroot/data/dog.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sps014/BlazorML5/53a250024eead2168d2af91ad07c4307cab3a14b/SampleApp/wwwroot/data/dog.jpg -------------------------------------------------------------------------------- /SampleApp/wwwroot/data/pose.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sps014/BlazorML5/53a250024eead2168d2af91ad07c4307cab3a14b/SampleApp/wwwroot/data/pose.jpg -------------------------------------------------------------------------------- /SampleApp/wwwroot/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sps014/BlazorML5/53a250024eead2168d2af91ad07c4307cab3a14b/SampleApp/wwwroot/favicon.ico -------------------------------------------------------------------------------- /SampleApp/wwwroot/icon-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sps014/BlazorML5/53a250024eead2168d2af91ad07c4307cab3a14b/SampleApp/wwwroot/icon-192.png -------------------------------------------------------------------------------- /SampleApp/wwwroot/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | SampleApp 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | Loading... 18 | 19 | 20 | An unhandled error has occurred. 21 | Reload 22 | 🗙 23 | 24 | 25 | 26 | 27 | 28 | --------------------------------------------------------------------------------
Sorry, there's nothing at this address.