├── .gitignore ├── AFECS_Video_Preview.png ├── AzureFunctions.Extensions.CognitiveServices.sln ├── LICENSE ├── README.md ├── logo.png ├── samples └── AzureFunctions.Extensions.CognitiveServics.Samples │ ├── .gitignore │ ├── AzureFunctions.Extensions.CognitiveServics.Samples.csproj │ ├── CognitiveServicesFunctions.cs │ ├── ThumbnailGenerator.cs │ ├── VisionResult.cs │ └── host.json ├── src ├── AzureFunctions.Extensions.CognitiveServices.sln └── AzureFunctions.Extensions.CognitiveServices │ ├── AzureFunctions.Extensions.CognitiveServices.csproj │ ├── Bindings │ └── Vision │ │ ├── Analysis │ │ ├── VisionAnalysisAttribute.cs │ │ ├── VisionAnalysisBinding.cs │ │ ├── VisionAnalysisClient.cs │ │ ├── VisionAnalysisExtension.cs │ │ ├── VisionAnalysisModel.cs │ │ ├── VisionAnalysisRequest.cs │ │ └── VisionAnalysisStartup.cs │ │ ├── Describe │ │ ├── VisionDescribeAttribute.cs │ │ ├── VisionDescribeBinding.cs │ │ ├── VisionDescribeClient.cs │ │ ├── VisionDescribeExtension.cs │ │ ├── VisionDescribeModel.cs │ │ ├── VisionDescribeRequest.cs │ │ └── VisionDescribeStartup.cs │ │ ├── Domain │ │ ├── VisionDomainAttribute.cs │ │ ├── VisionDomainBinding.cs │ │ ├── VisionDomainCelebrityModel.cs │ │ ├── VisionDomainClient.cs │ │ ├── VisionDomainExtension.cs │ │ ├── VisionDomainLandmarkModel.cs │ │ ├── VisionDomainRequest.cs │ │ └── VisionDomainStartup.cs │ │ ├── Handwriting │ │ ├── VisionHandwritingAttribute.cs │ │ ├── VisionHandwritingBinding.cs │ │ ├── VisionHandwritingClient.cs │ │ ├── VisionHandwritingExtension.cs │ │ ├── VisionHandwritingModel.cs │ │ ├── VisionHandwritingRequest.cs │ │ └── VisionHandwritingStartup.cs │ │ ├── IVisionBinding.cs │ │ ├── Ocr │ │ ├── VisionOCRAttribute.cs │ │ ├── VisionOCRBinding.cs │ │ ├── VisionOCRClient.cs │ │ ├── VisionOCRExtension.cs │ │ ├── VisionOCRRequest.cs │ │ ├── VisionOCRStartup.cs │ │ └── VisionOcrModel.cs │ │ ├── Thumbnail │ │ ├── VisionThumbnailAttribute.cs │ │ ├── VisionThumbnailBinding.cs │ │ ├── VisionThumbnailClient.cs │ │ ├── VisionThumbnailExtension.cs │ │ ├── VisionThumbnailRequest.cs │ │ └── VisionThumbnailStartup.cs │ │ ├── VisionAttributeBase.cs │ │ ├── VisionErrorModel.cs │ │ ├── VisionRequestBase.cs │ │ └── VisionUrlRequest.cs │ ├── Config │ ├── RetryPolicy.cs │ └── VisionConfiguration.cs │ ├── Properties │ └── AssemblyInfo.cs │ └── Services │ ├── CognitiveServicesClient.cs │ ├── ICognitiveServicesClient.cs │ ├── ImageResizeService.cs │ ├── Models │ └── ServiceResultModel.cs │ └── StorageServices.cs └── tests └── AzureFunctions.Extensions.CognitiveServices.Tests ├── AzureFunctions.Extensions.CognitiveServices.Tests.csproj ├── Common ├── ExplicitTypeLocator.cs ├── FunctionHost.cs ├── LogMessage.cs ├── TestCognitiveServicesClient.cs ├── TestHelper.cs ├── TestLogger.cs ├── TestLoggerProvider.cs └── TestNameResolver.cs ├── Resources ├── MockResults.Designer.cs ├── MockResults.resx ├── SamplePhoto.jpeg ├── SamplePhotoTooBig.jpg ├── VisionAnalysisResults.json └── VisionDescribeResults.json ├── TestHelper.cs ├── TestHelpers └── VerboseDiagnosticsTraceWriter.cs ├── VisionAnalysisTests.cs ├── VisionDescribeTests.cs ├── VisionDomainTests.cs ├── VisionHandwritingTests.cs ├── VisionOcrTests.cs ├── VisionThumbnailTests.cs └── local.settings.json /.gitignore: -------------------------------------------------------------------------------- 1 | ################################################################################ 2 | ## Ignore Visual Studio temporary files, build results, and 3 | ## files generated by popular Visual Studio add-ons. 4 | ## 5 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 6 | 7 | # User-specific files 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Build results 17 | [Dd]ebug/ 18 | [Dd]ebugPublic/ 19 | [Rr]elease/ 20 | [Rr]eleases/ 21 | x64/ 22 | x86/ 23 | bld/ 24 | [Bb]in/ 25 | [Oo]bj/ 26 | [Ll]og/ 27 | 28 | # Visual Studio 2015/2017 cache/options directory 29 | .vs/ 30 | # Uncomment if you have tasks that create the project's static files in wwwroot 31 | #wwwroot/ 32 | 33 | # Visual Studio 2017 auto generated files 34 | Generated\ Files/ 35 | 36 | # MSTest test Results 37 | [Tt]est[Rr]esult*/ 38 | [Bb]uild[Ll]og.* 39 | 40 | # NUNIT 41 | *.VisualState.xml 42 | TestResult.xml 43 | 44 | # Build Results of an ATL Project 45 | [Dd]ebugPS/ 46 | [Rr]eleasePS/ 47 | dlldata.c 48 | 49 | # Benchmark Results 50 | BenchmarkDotNet.Artifacts/ 51 | 52 | # .NET Core 53 | project.lock.json 54 | project.fragment.lock.json 55 | artifacts/ 56 | **/Properties/launchSettings.json 57 | 58 | # StyleCop 59 | StyleCopReport.xml 60 | 61 | # Files built by Visual Studio 62 | *_i.c 63 | *_p.c 64 | *_i.h 65 | *.ilk 66 | *.meta 67 | *.obj 68 | *.iobj 69 | *.pch 70 | *.pdb 71 | *.ipdb 72 | *.pgc 73 | *.pgd 74 | *.rsp 75 | *.sbr 76 | *.tlb 77 | *.tli 78 | *.tlh 79 | *.tmp 80 | *.tmp_proj 81 | *.log 82 | *.vspscc 83 | *.vssscc 84 | .builds 85 | *.pidb 86 | *.svclog 87 | *.scc 88 | 89 | # Chutzpah Test files 90 | _Chutzpah* 91 | 92 | # Visual C++ cache files 93 | ipch/ 94 | *.aps 95 | *.ncb 96 | *.opendb 97 | *.opensdf 98 | *.sdf 99 | *.cachefile 100 | *.VC.db 101 | *.VC.VC.opendb 102 | 103 | # Visual Studio profiler 104 | *.psess 105 | *.vsp 106 | *.vspx 107 | *.sap 108 | 109 | # Visual Studio Trace Files 110 | *.e2e 111 | 112 | # TFS 2012 Local Workspace 113 | $tf/ 114 | 115 | # Guidance Automation Toolkit 116 | *.gpState 117 | 118 | # ReSharper is a .NET coding add-in 119 | _ReSharper*/ 120 | *.[Rr]e[Ss]harper 121 | *.DotSettings.user 122 | 123 | # JustCode is a .NET coding add-in 124 | .JustCode 125 | 126 | # TeamCity is a build add-in 127 | _TeamCity* 128 | 129 | # DotCover is a Code Coverage Tool 130 | *.dotCover 131 | 132 | # AxoCover is a Code Coverage Tool 133 | .axoCover/* 134 | !.axoCover/settings.json 135 | 136 | # Visual Studio code coverage results 137 | *.coverage 138 | *.coveragexml 139 | 140 | # NCrunch 141 | _NCrunch_* 142 | .*crunch*.local.xml 143 | nCrunchTemp_* 144 | 145 | # MightyMoose 146 | *.mm.* 147 | AutoTest.Net/ 148 | 149 | # Web workbench (sass) 150 | .sass-cache/ 151 | 152 | # Installshield output folder 153 | [Ee]xpress/ 154 | 155 | # DocProject is a documentation generator add-in 156 | DocProject/buildhelp/ 157 | DocProject/Help/*.HxT 158 | DocProject/Help/*.HxC 159 | DocProject/Help/*.hhc 160 | DocProject/Help/*.hhk 161 | DocProject/Help/*.hhp 162 | DocProject/Help/Html2 163 | DocProject/Help/html 164 | 165 | # Click-Once directory 166 | publish/ 167 | 168 | # Publish Web Output 169 | *.[Pp]ublish.xml 170 | *.azurePubxml 171 | # Note: Comment the next line if you want to checkin your web deploy settings, 172 | # but database connection strings (with potential passwords) will be unencrypted 173 | *.pubxml 174 | *.publishproj 175 | 176 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 177 | # checkin your Azure Web App publish settings, but sensitive information contained 178 | # in these scripts will be unencrypted 179 | PublishScripts/ 180 | 181 | # NuGet Packages 182 | *.nupkg 183 | # The packages folder can be ignored because of Package Restore 184 | **/[Pp]ackages/* 185 | # except build/, which is used as an MSBuild target. 186 | !**/[Pp]ackages/build/ 187 | # Uncomment if necessary however generally it will be regenerated when needed 188 | #!**/[Pp]ackages/repositories.config 189 | # NuGet v3's project.json files produces more ignorable files 190 | *.nuget.props 191 | *.nuget.targets 192 | 193 | # Microsoft Azure Build Output 194 | csx/ 195 | *.build.csdef 196 | 197 | # Microsoft Azure Emulator 198 | ecf/ 199 | rcf/ 200 | 201 | # Windows Store app package directories and files 202 | AppPackages/ 203 | BundleArtifacts/ 204 | Package.StoreAssociation.xml 205 | _pkginfo.txt 206 | *.appx 207 | 208 | # Visual Studio cache files 209 | # files ending in .cache can be ignored 210 | *.[Cc]ache 211 | # but keep track of directories ending in .cache 212 | !*.[Cc]ache/ 213 | 214 | # Others 215 | ClientBin/ 216 | ~$* 217 | *~ 218 | *.dbmdl 219 | *.dbproj.schemaview 220 | *.jfm 221 | *.pfx 222 | *.publishsettings 223 | orleans.codegen.cs 224 | 225 | # Including strong name files can present a security risk 226 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 227 | #*.snk 228 | 229 | # Since there are multiple workflows, uncomment next line to ignore bower_components 230 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 231 | #bower_components/ 232 | 233 | # RIA/Silverlight projects 234 | Generated_Code/ 235 | 236 | # Backup & report files from converting an old project file 237 | # to a newer Visual Studio version. Backup files are not needed, 238 | # because we have git ;-) 239 | _UpgradeReport_Files/ 240 | Backup*/ 241 | UpgradeLog*.XML 242 | UpgradeLog*.htm 243 | ServiceFabricBackup/ 244 | *.rptproj.bak 245 | 246 | # SQL Server files 247 | *.mdf 248 | *.ldf 249 | *.ndf 250 | 251 | # Business Intelligence projects 252 | *.rdl.data 253 | *.bim.layout 254 | *.bim_*.settings 255 | *.rptproj.rsuser 256 | 257 | # Microsoft Fakes 258 | FakesAssemblies/ 259 | 260 | # GhostDoc plugin setting file 261 | *.GhostDoc.xml 262 | 263 | # Node.js Tools for Visual Studio 264 | .ntvs_analysis.dat 265 | node_modules/ 266 | 267 | # Visual Studio 6 build log 268 | *.plg 269 | 270 | # Visual Studio 6 workspace options file 271 | *.opt 272 | 273 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 274 | *.vbw 275 | 276 | # Visual Studio LightSwitch build output 277 | **/*.HTMLClient/GeneratedArtifacts 278 | **/*.DesktopClient/GeneratedArtifacts 279 | **/*.DesktopClient/ModelManifest.xml 280 | **/*.Server/GeneratedArtifacts 281 | **/*.Server/ModelManifest.xml 282 | _Pvt_Extensions 283 | 284 | # Paket dependency manager 285 | .paket/paket.exe 286 | paket-files/ 287 | 288 | # FAKE - F# Make 289 | .fake/ 290 | 291 | # JetBrains Rider 292 | .idea/ 293 | *.sln.iml 294 | 295 | # CodeRush 296 | .cr/ 297 | 298 | # Python Tools for Visual Studio (PTVS) 299 | __pycache__/ 300 | *.pyc 301 | 302 | # Cake - Uncomment if you are using it 303 | # tools/** 304 | # !tools/packages.config 305 | 306 | # Tabs Studio 307 | *.tss 308 | 309 | # Telerik's JustMock configuration file 310 | *.jmconfig 311 | 312 | # BizTalk build output 313 | *.btp.cs 314 | *.btm.cs 315 | *.odx.cs 316 | *.xsd.cs 317 | 318 | # OpenCover UI analysis results 319 | OpenCover/ 320 | 321 | # Azure Stream Analytics local run output 322 | ASALocalRun/ 323 | 324 | # MSBuild Binary and Structured Log 325 | *.binlog 326 | 327 | # NVidia Nsight GPU debugger configuration file 328 | *.nvuser 329 | 330 | # MFractors (Xamarin productivity tool) working folder 331 | .mfractor/ 332 | -------------------------------------------------------------------------------- /AFECS_Video_Preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joshdcar/azure-functions-extensions-cognitive-services/f6dea9eff6c8d5a9cd1bb99fe21aa63efefac895/AFECS_Video_Preview.png -------------------------------------------------------------------------------- /AzureFunctions.Extensions.CognitiveServices.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.27428.1 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{71539E00-8C5F-4B45-AA0D-B83CAC58FCA4}" 7 | EndProject 8 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{862626B4-433B-4FA4-96DC-815850960814}" 9 | EndProject 10 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AzureFunctions.Extensions.CognitiveServics.Samples", "samples\AzureFunctions.Extensions.CognitiveServics.Samples\AzureFunctions.Extensions.CognitiveServics.Samples.csproj", "{78916DA5-CFEC-40B5-84A6-B0190B076700}" 11 | EndProject 12 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{9936F693-95E7-48C4-ADC3-3FFCBC654587}" 13 | EndProject 14 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AzureFunctions.Extensions.CognitiveServices", "src\AzureFunctions.Extensions.CognitiveServices\AzureFunctions.Extensions.CognitiveServices.csproj", "{90EED71C-45C7-4952-818B-3643A3E906B9}" 15 | EndProject 16 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{3F518617-3B7F-44DD-B6C3-51A55FA76D5C}" 17 | ProjectSection(SolutionItems) = preProject 18 | logo.png = logo.png 19 | README.md = README.md 20 | EndProjectSection 21 | EndProject 22 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AzureFunctions.Extensions.CognitiveServices.Tests", "tests\AzureFunctions.Extensions.CognitiveServices.Tests\AzureFunctions.Extensions.CognitiveServices.Tests.csproj", "{50FAB084-7357-4943-9186-0C3F81CC9F3E}" 23 | EndProject 24 | Global 25 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 26 | Debug|Any CPU = Debug|Any CPU 27 | Release|Any CPU = Release|Any CPU 28 | EndGlobalSection 29 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 30 | {78916DA5-CFEC-40B5-84A6-B0190B076700}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 31 | {78916DA5-CFEC-40B5-84A6-B0190B076700}.Debug|Any CPU.Build.0 = Debug|Any CPU 32 | {78916DA5-CFEC-40B5-84A6-B0190B076700}.Release|Any CPU.ActiveCfg = Release|Any CPU 33 | {78916DA5-CFEC-40B5-84A6-B0190B076700}.Release|Any CPU.Build.0 = Release|Any CPU 34 | {90EED71C-45C7-4952-818B-3643A3E906B9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 35 | {90EED71C-45C7-4952-818B-3643A3E906B9}.Debug|Any CPU.Build.0 = Debug|Any CPU 36 | {90EED71C-45C7-4952-818B-3643A3E906B9}.Release|Any CPU.ActiveCfg = Release|Any CPU 37 | {90EED71C-45C7-4952-818B-3643A3E906B9}.Release|Any CPU.Build.0 = Release|Any CPU 38 | {50FAB084-7357-4943-9186-0C3F81CC9F3E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 39 | {50FAB084-7357-4943-9186-0C3F81CC9F3E}.Debug|Any CPU.Build.0 = Debug|Any CPU 40 | {50FAB084-7357-4943-9186-0C3F81CC9F3E}.Release|Any CPU.ActiveCfg = Release|Any CPU 41 | {50FAB084-7357-4943-9186-0C3F81CC9F3E}.Release|Any CPU.Build.0 = Release|Any CPU 42 | EndGlobalSection 43 | GlobalSection(SolutionProperties) = preSolution 44 | HideSolutionNode = FALSE 45 | EndGlobalSection 46 | GlobalSection(NestedProjects) = preSolution 47 | {78916DA5-CFEC-40B5-84A6-B0190B076700} = {862626B4-433B-4FA4-96DC-815850960814} 48 | {90EED71C-45C7-4952-818B-3643A3E906B9} = {9936F693-95E7-48C4-ADC3-3FFCBC654587} 49 | {50FAB084-7357-4943-9186-0C3F81CC9F3E} = {71539E00-8C5F-4B45-AA0D-B83CAC58FCA4} 50 | EndGlobalSection 51 | GlobalSection(ExtensibilityGlobals) = postSolution 52 | SolutionGuid = {49F1B715-24A3-4580-BA87-34ED114ED2B8} 53 | EndGlobalSection 54 | EndGlobal 55 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Josh Carlisle 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 | -------------------------------------------------------------------------------- /logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joshdcar/azure-functions-extensions-cognitive-services/f6dea9eff6c8d5a9cd1bb99fe21aa63efefac895/logo.png -------------------------------------------------------------------------------- /samples/AzureFunctions.Extensions.CognitiveServics.Samples/.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # Azure Functions localsettings file 5 | local.settings.json 6 | 7 | # User-specific files 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Build results 17 | [Dd]ebug/ 18 | [Dd]ebugPublic/ 19 | [Rr]elease/ 20 | [Rr]eleases/ 21 | x64/ 22 | x86/ 23 | bld/ 24 | [Bb]in/ 25 | [Oo]bj/ 26 | [Ll]og/ 27 | 28 | # Visual Studio 2015 cache/options directory 29 | .vs/ 30 | # Uncomment if you have tasks that create the project's static files in wwwroot 31 | #wwwroot/ 32 | 33 | # MSTest test Results 34 | [Tt]est[Rr]esult*/ 35 | [Bb]uild[Ll]og.* 36 | 37 | # NUNIT 38 | *.VisualState.xml 39 | TestResult.xml 40 | 41 | # Build Results of an ATL Project 42 | [Dd]ebugPS/ 43 | [Rr]eleasePS/ 44 | dlldata.c 45 | 46 | # DNX 47 | project.lock.json 48 | project.fragment.lock.json 49 | artifacts/ 50 | 51 | *_i.c 52 | *_p.c 53 | *_i.h 54 | *.ilk 55 | *.meta 56 | *.obj 57 | *.pch 58 | *.pdb 59 | *.pgc 60 | *.pgd 61 | *.rsp 62 | *.sbr 63 | *.tlb 64 | *.tli 65 | *.tlh 66 | *.tmp 67 | *.tmp_proj 68 | *.log 69 | *.vspscc 70 | *.vssscc 71 | .builds 72 | *.pidb 73 | *.svclog 74 | *.scc 75 | 76 | # Chutzpah Test files 77 | _Chutzpah* 78 | 79 | # Visual C++ cache files 80 | ipch/ 81 | *.aps 82 | *.ncb 83 | *.opendb 84 | *.opensdf 85 | *.sdf 86 | *.cachefile 87 | *.VC.db 88 | *.VC.VC.opendb 89 | 90 | # Visual Studio profiler 91 | *.psess 92 | *.vsp 93 | *.vspx 94 | *.sap 95 | 96 | # TFS 2012 Local Workspace 97 | $tf/ 98 | 99 | # Guidance Automation Toolkit 100 | *.gpState 101 | 102 | # ReSharper is a .NET coding add-in 103 | _ReSharper*/ 104 | *.[Rr]e[Ss]harper 105 | *.DotSettings.user 106 | 107 | # JustCode is a .NET coding add-in 108 | .JustCode 109 | 110 | # TeamCity is a build add-in 111 | _TeamCity* 112 | 113 | # DotCover is a Code Coverage Tool 114 | *.dotCover 115 | 116 | # NCrunch 117 | _NCrunch_* 118 | .*crunch*.local.xml 119 | nCrunchTemp_* 120 | 121 | # MightyMoose 122 | *.mm.* 123 | AutoTest.Net/ 124 | 125 | # Web workbench (sass) 126 | .sass-cache/ 127 | 128 | # Installshield output folder 129 | [Ee]xpress/ 130 | 131 | # DocProject is a documentation generator add-in 132 | DocProject/buildhelp/ 133 | DocProject/Help/*.HxT 134 | DocProject/Help/*.HxC 135 | DocProject/Help/*.hhc 136 | DocProject/Help/*.hhk 137 | DocProject/Help/*.hhp 138 | DocProject/Help/Html2 139 | DocProject/Help/html 140 | 141 | # Click-Once directory 142 | publish/ 143 | 144 | # Publish Web Output 145 | *.[Pp]ublish.xml 146 | *.azurePubxml 147 | # TODO: Comment the next line if you want to checkin your web deploy settings 148 | # but database connection strings (with potential passwords) will be unencrypted 149 | #*.pubxml 150 | *.publishproj 151 | 152 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 153 | # checkin your Azure Web App publish settings, but sensitive information contained 154 | # in these scripts will be unencrypted 155 | PublishScripts/ 156 | 157 | # NuGet Packages 158 | *.nupkg 159 | # The packages folder can be ignored because of Package Restore 160 | **/packages/* 161 | # except build/, which is used as an MSBuild target. 162 | !**/packages/build/ 163 | # Uncomment if necessary however generally it will be regenerated when needed 164 | #!**/packages/repositories.config 165 | # NuGet v3's project.json files produces more ignoreable files 166 | *.nuget.props 167 | *.nuget.targets 168 | 169 | # Microsoft Azure Build Output 170 | csx/ 171 | *.build.csdef 172 | 173 | # Microsoft Azure Emulator 174 | ecf/ 175 | rcf/ 176 | 177 | # Windows Store app package directories and files 178 | AppPackages/ 179 | BundleArtifacts/ 180 | Package.StoreAssociation.xml 181 | _pkginfo.txt 182 | 183 | # Visual Studio cache files 184 | # files ending in .cache can be ignored 185 | *.[Cc]ache 186 | # but keep track of directories ending in .cache 187 | !*.[Cc]ache/ 188 | 189 | # Others 190 | ClientBin/ 191 | ~$* 192 | *~ 193 | *.dbmdl 194 | *.dbproj.schemaview 195 | *.jfm 196 | *.pfx 197 | *.publishsettings 198 | node_modules/ 199 | orleans.codegen.cs 200 | 201 | # Since there are multiple workflows, uncomment next line to ignore bower_components 202 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 203 | #bower_components/ 204 | 205 | # RIA/Silverlight projects 206 | Generated_Code/ 207 | 208 | # Backup & report files from converting an old project file 209 | # to a newer Visual Studio version. Backup files are not needed, 210 | # because we have git ;-) 211 | _UpgradeReport_Files/ 212 | Backup*/ 213 | UpgradeLog*.XML 214 | UpgradeLog*.htm 215 | 216 | # SQL Server files 217 | *.mdf 218 | *.ldf 219 | 220 | # Business Intelligence projects 221 | *.rdl.data 222 | *.bim.layout 223 | *.bim_*.settings 224 | 225 | # Microsoft Fakes 226 | FakesAssemblies/ 227 | 228 | # GhostDoc plugin setting file 229 | *.GhostDoc.xml 230 | 231 | # Node.js Tools for Visual Studio 232 | .ntvs_analysis.dat 233 | 234 | # Visual Studio 6 build log 235 | *.plg 236 | 237 | # Visual Studio 6 workspace options file 238 | *.opt 239 | 240 | # Visual Studio LightSwitch build output 241 | **/*.HTMLClient/GeneratedArtifacts 242 | **/*.DesktopClient/GeneratedArtifacts 243 | **/*.DesktopClient/ModelManifest.xml 244 | **/*.Server/GeneratedArtifacts 245 | **/*.Server/ModelManifest.xml 246 | _Pvt_Extensions 247 | 248 | # Paket dependency manager 249 | .paket/paket.exe 250 | paket-files/ 251 | 252 | # FAKE - F# Make 253 | .fake/ 254 | 255 | # JetBrains Rider 256 | .idea/ 257 | *.sln.iml 258 | 259 | # CodeRush 260 | .cr/ 261 | 262 | # Python Tools for Visual Studio (PTVS) 263 | __pycache__/ 264 | *.pyc -------------------------------------------------------------------------------- /samples/AzureFunctions.Extensions.CognitiveServics.Samples/AzureFunctions.Extensions.CognitiveServics.Samples.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | netstandard2.0 4 | v2 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | PreserveNewest 16 | 17 | 18 | PreserveNewest 19 | Never 20 | 21 | 22 | -------------------------------------------------------------------------------- /samples/AzureFunctions.Extensions.CognitiveServics.Samples/CognitiveServicesFunctions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Threading.Tasks; 4 | using AzureFunctions.Extensions.CognitiveServices.Bindings.Vision.Analysis; 5 | using AzureFunctions.Extensions.CognitiveServices.Bindings.Vision.Describe; 6 | using AzureFunctions.Extensions.CognitiveServices.Bindings.Vision.Domain; 7 | using AzureFunctions.Extensions.CognitiveServices.Bindings.Vision.Handwriting; 8 | using AzureFunctions.Extensions.CognitiveServices.Bindings.Vision.Ocr; 9 | using AzureFunctions.Extensions.CognitiveServices.Bindings.Vision.Thumbnail; 10 | using AzureFunctions.Extensions.CognitiveServices.Bindings; 11 | using Microsoft.Azure.WebJobs; 12 | using Microsoft.Azure.WebJobs.Host; 13 | 14 | namespace AzureFunctions.Extensions.CognitiveServics.Samples 15 | { 16 | [StorageAccount("storageaccount")] 17 | public static class CognitiveServicesFunctions 18 | { 19 | 20 | #region Vision Analysis 21 | 22 | /// 23 | /// Sample calling Vision Analysis Triggered from a blob storage 24 | /// Trigger: Blob Storage 25 | /// Vision Binding: Model Binding w/ Blob Data Source 26 | /// 27 | /// 28 | /// 29 | /// 30 | /// 31 | /// 32 | [FunctionName("VisionAnalysisModelBlobFunction")] 33 | public static async Task VisionAnalysisModelBlobFunctionRun( 34 | [BlobTrigger("analysismodel/{name}")]Stream storageBlob, 35 | [VisionAnalysis(BlobStorageAccount = "StorageAccount", 36 | BlobStoragePath = "analysismodel/{name}", 37 | ImageSource = ImageSource.BlobStorage)]VisionAnalysisModel result, 38 | string name, 39 | TraceWriter log) 40 | { 41 | 42 | log.Info($"Analysis Results:{result}"); 43 | 44 | } 45 | 46 | 47 | [FunctionName("VisionAnalysisBlobFunction")] 48 | public static async Task VisionAnalysisRun( 49 | [BlobTrigger("analysis/{name}")]Stream storageBlob, 50 | [Table("VisionResults")]IAsyncCollector results, 51 | [VisionAnalysis()]VisionAnalysisClient visionclient, 52 | string name, 53 | TraceWriter log) 54 | { 55 | 56 | var result = await visionclient.AnalyzeAsync(new VisionAnalysisRequest(storageBlob)); 57 | 58 | await results.AddAsync(new VisionResult(Guid.NewGuid().ToString(), "VisionAnalysis") { ResultJson = result.ToString() }); 59 | 60 | log.Info($"Analysis Results:{result.ToString()}"); 61 | 62 | } 63 | 64 | #endregion 65 | 66 | 67 | [FunctionName("VisionDesribeBlobFunction")] 68 | public static async Task VisionDescribeBlobFunction( 69 | [BlobTrigger("describe/{name}")]Stream storageBlob, 70 | [Table("VisionResults")]IAsyncCollector results, 71 | [VisionDescribe()]VisionDescribeClient visionclient, 72 | string name, 73 | TraceWriter log 74 | ) 75 | { 76 | var result = await visionclient.DescribeAsync(new VisionDescribeRequest(storageBlob)); 77 | 78 | await results.AddAsync(new VisionResult(Guid.NewGuid().ToString(), "VisionDescribe") { ResultJson = result.ToString() }); 79 | 80 | log.Info($"Describe Results:{result.ToString()}"); 81 | 82 | } 83 | 84 | [FunctionName("VisionDescribeModelBlobFunction")] 85 | public static async Task VisionDescribeModelBlobFunction( 86 | [BlobTrigger("describe/{name}")]Stream storageBlob, 87 | [Table("VisionResults")]IAsyncCollector results, 88 | [VisionDescribe(BlobStorageAccount = "StorageAccount", 89 | BlobStoragePath = "describemodel/{name}", 90 | ImageSource = ImageSource.BlobStorage)]VisionDescribeModel result, 91 | string name, 92 | TraceWriter log 93 | ) 94 | { 95 | log.Info($"Describe Results:{result.ToString()}"); 96 | } 97 | 98 | [FunctionName("VisionThumbnailBlobFunction")] 99 | public static async Task VisionThumbnailBlobFunction( 100 | [BlobTrigger("thumbnail/{name}")]Stream storageBlob, 101 | [VisionThumbnail(AutoResize = true, 102 | Height ="100", 103 | Width = "100", 104 | SmartCropping =true)]VisionThumbnailClient visionclient, 105 | [Blob("thumbnailresults/{name}", FileAccess.Write)]Stream thumbnailBlob, 106 | string name, 107 | TraceWriter log 108 | ) 109 | { 110 | 111 | var result = await visionclient.ThumbnailAsync(new VisionThumbnailRequest(storageBlob)); 112 | 113 | using (MemoryStream stream = new MemoryStream(result)) 114 | { 115 | await stream.CopyToAsync(thumbnailBlob); 116 | } 117 | 118 | log.Info($"Image thumbnail generated"); 119 | 120 | } 121 | 122 | [FunctionName("VisionOcrBlobFunction")] 123 | public static async Task VisionOcrBlobFunction( 124 | [BlobTrigger("ocrrequest/{name}")]Stream storageBlob, 125 | [Table("VisionResults")]IAsyncCollector results, 126 | [VisionOcr()]VisionOcrClient visionclient, 127 | string name, 128 | TraceWriter log 129 | ) 130 | { 131 | var result = await visionclient.OCRAsync(new VisionOcrRequest(storageBlob)); 132 | 133 | await results.AddAsync(new VisionResult(Guid.NewGuid().ToString(), "VisionOCR") { ResultJson = result.ToString() }); 134 | 135 | log.Info($"OCR Results:{result.ToString()}"); 136 | 137 | } 138 | 139 | [FunctionName("VisionHandwritingBlobFunction")] 140 | public static async Task VisionHandwritingBlobFunction( 141 | [BlobTrigger("handwriting/{name}")]Stream storageBlob, 142 | [Table("VisionResults")]IAsyncCollector results, 143 | [VisionHandwriting()]VisionHandwritingClient visionclient, 144 | string name, 145 | TraceWriter log 146 | ) 147 | { 148 | var result = await visionclient.HandwritingAsync(new VisionHandwritingRequest(storageBlob)); 149 | 150 | await results.AddAsync(new VisionResult(Guid.NewGuid().ToString(), "VisionHandwriting") { ResultJson = result.ToString() }); 151 | 152 | log.Info($"Handwriting Results:{result.ToString()}"); 153 | 154 | } 155 | 156 | [FunctionName("VisionCelebrityBlobFunction")] 157 | public static async Task VisionCelebrityBlobFunction( 158 | [BlobTrigger("celebrity/{name}")]Stream storageBlob, 159 | [Table("VisionResults")]IAsyncCollector results, 160 | [VisionDomain(Domain = VisionDomainRequest.CELEBRITY_DOMAIN)]VisionDomainClient visionclient, 161 | string name, 162 | TraceWriter log 163 | ) 164 | { 165 | var request = new VisionDomainRequest(storageBlob) { Domain = VisionDomainOptions.Celebrity }; 166 | 167 | var celebrityResult = await visionclient.AnalyzeCelebrityAsync(request); 168 | 169 | await results.AddAsync(new VisionResult(Guid.NewGuid().ToString(), "VisionDomain") { ResultJson = celebrityResult.ToString() }); 170 | 171 | log.Info($"Celebrity Domain results:{celebrityResult.ToString()}"); 172 | 173 | 174 | } 175 | 176 | [FunctionName("VisionLandmarkBlobFunction")] 177 | public static async Task VisionLandmarkBlobFunction( 178 | [BlobTrigger("landmarks/{name}")]Stream storageBlob, 179 | [Table("VisionResults")]IAsyncCollector results, 180 | [VisionDomain(Domain = VisionDomainRequest.LANDMARK_DOMAIN)]VisionDomainClient visionclient, 181 | string name, 182 | TraceWriter log 183 | ) 184 | { 185 | var landmarkResult = await visionclient.AnalyzeLandmarkAsync(new VisionDomainRequest(storageBlob)); 186 | 187 | await results.AddAsync(new VisionResult(Guid.NewGuid().ToString(), "VisionDomain") { ResultJson = landmarkResult.ToString() }); 188 | 189 | log.Info($"Celebrity Domain results:{landmarkResult.ToString()}"); 190 | 191 | 192 | } 193 | } 194 | } 195 | -------------------------------------------------------------------------------- /samples/AzureFunctions.Extensions.CognitiveServics.Samples/ThumbnailGenerator.cs: -------------------------------------------------------------------------------- 1 | 2 | using System.IO; 3 | using Microsoft.AspNetCore.Mvc; 4 | using Microsoft.Azure.WebJobs; 5 | using Microsoft.Azure.WebJobs.Extensions.Http; 6 | using Microsoft.AspNetCore.Http; 7 | using Microsoft.Azure.WebJobs.Host; 8 | using Newtonsoft.Json; 9 | using System.Threading.Tasks; 10 | using AzureFunctions.Extensions.CognitiveServices.Bindings.Vision.Thumbnail; 11 | using AzureFunctions.Extensions.CognitiveServices.Bindings; 12 | 13 | namespace AzureFunctions.Extensions.CognitiveServics.Samples 14 | { 15 | public static class ThumbnailGenerator 16 | { 17 | 18 | 19 | [StorageAccount("StorageAccount")] 20 | [FunctionName("ThumbnailGenApi")] 21 | public static async Task ThumbnailGenApi( 22 | [HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = "generatethumbnail/{filename}")]HttpRequest req, 23 | [Blob("images/{filename}", FileAccess.Write)]Stream imageStream, 24 | [Blob("thumbnails/{filename}", FileAccess.Write)]Stream thumbnailImageStream, 25 | [VisionThumbnail(SmartCropping = true, Height = "150", Width = "150")]VisionThumbnailClient client, 26 | string fileName, 27 | TraceWriter log) 28 | { 29 | 30 | //Load Http Request Body into Thumbnail Request 31 | 32 | var request = new VisionThumbnailRequest(req.Body); 33 | 34 | //Generate Thumbnail 35 | var thumbnailBytes = await client.ThumbnailAsync(request); 36 | 37 | //Output Original Image to Blob Storage 38 | using (MemoryStream stream = new MemoryStream(request.ImageBytes)) 39 | { 40 | await stream.CopyToAsync(imageStream); 41 | } 42 | 43 | //Output Thumbnail To Blob Storage 44 | using (MemoryStream stream = new MemoryStream(thumbnailBytes)) 45 | { 46 | await stream.CopyToAsync(thumbnailImageStream); 47 | } 48 | 49 | 50 | return (ActionResult)new OkResult(); 51 | 52 | } 53 | 54 | [StorageAccount("StorageAccount")] 55 | [FunctionName("ThumbnailGenBlobTrigger")] 56 | public static void ThumbnailGenBlobTrigger( 57 | 58 | [BlobTrigger("myimages/{filename}")]byte[] image, 59 | [Blob("mythumbnails/{filename}", FileAccess.Write)]out byte[] thumbnailImage, 60 | [VisionThumbnail(ImageSource=ImageSource.BlobStorage, 61 | BlobStorageAccount ="StorageAccount", 62 | BlobStoragePath ="images/{filename}", 63 | SmartCropping = true, 64 | Height = "150", 65 | Width = "150")]byte[] thumbnail, 66 | string fileName, 67 | TraceWriter log) 68 | { 69 | //Output thumbnail 70 | thumbnailImage = thumbnail; 71 | 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /samples/AzureFunctions.Extensions.CognitiveServics.Samples/VisionResult.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.WindowsAzure.Storage.Table; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | 6 | namespace AzureFunctions.Extensions.CognitiveServics.Samples 7 | { 8 | public class VisionResult : TableEntity 9 | { 10 | 11 | public VisionResult(string id, string partitionKey) 12 | { 13 | this.RowKey = id; 14 | this.PartitionKey = partitionKey; 15 | } 16 | 17 | public string ResultJson { get; set; } 18 | 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /samples/AzureFunctions.Extensions.CognitiveServics.Samples/host.json: -------------------------------------------------------------------------------- 1 | { 2 | } -------------------------------------------------------------------------------- /src/AzureFunctions.Extensions.CognitiveServices.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.27428.1 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AzureFunctions.Extensions.CognitiveServices", "AzureFunctions.Extensions.CognitiveServices\AzureFunctions.Extensions.CognitiveServices.csproj", "{CF825051-50F5-45C6-9220-E794DE51F87C}" 7 | EndProject 8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AzureFunctions.Extensions.CognitiveServices.Tests", "..\tests\AzureFunctions.Extensions.CognitiveServices.Tests\AzureFunctions.Extensions.CognitiveServices.Tests.csproj", "{37767F3C-FDD9-4F2F-842B-CB347CF46072}" 9 | EndProject 10 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AzureFunctions.Extensions.CognitiveServics.Samples", "..\samples\AzureFunctions.Extensions.CognitiveServics.Samples\AzureFunctions.Extensions.CognitiveServics.Samples.csproj", "{553593F1-CBBC-4555-BB82-616C12E0EC82}" 11 | EndProject 12 | Global 13 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 14 | Debug|Any CPU = Debug|Any CPU 15 | Release|Any CPU = Release|Any CPU 16 | EndGlobalSection 17 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 18 | {CF825051-50F5-45C6-9220-E794DE51F87C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 19 | {CF825051-50F5-45C6-9220-E794DE51F87C}.Debug|Any CPU.Build.0 = Debug|Any CPU 20 | {CF825051-50F5-45C6-9220-E794DE51F87C}.Release|Any CPU.ActiveCfg = Release|Any CPU 21 | {CF825051-50F5-45C6-9220-E794DE51F87C}.Release|Any CPU.Build.0 = Release|Any CPU 22 | {37767F3C-FDD9-4F2F-842B-CB347CF46072}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 23 | {37767F3C-FDD9-4F2F-842B-CB347CF46072}.Debug|Any CPU.Build.0 = Debug|Any CPU 24 | {37767F3C-FDD9-4F2F-842B-CB347CF46072}.Release|Any CPU.ActiveCfg = Release|Any CPU 25 | {37767F3C-FDD9-4F2F-842B-CB347CF46072}.Release|Any CPU.Build.0 = Release|Any CPU 26 | {553593F1-CBBC-4555-BB82-616C12E0EC82}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 27 | {553593F1-CBBC-4555-BB82-616C12E0EC82}.Debug|Any CPU.Build.0 = Debug|Any CPU 28 | {553593F1-CBBC-4555-BB82-616C12E0EC82}.Release|Any CPU.ActiveCfg = Release|Any CPU 29 | {553593F1-CBBC-4555-BB82-616C12E0EC82}.Release|Any CPU.Build.0 = Release|Any CPU 30 | EndGlobalSection 31 | GlobalSection(SolutionProperties) = preSolution 32 | HideSolutionNode = FALSE 33 | EndGlobalSection 34 | GlobalSection(ExtensibilityGlobals) = postSolution 35 | SolutionGuid = {49F1B715-24A3-4580-BA87-34ED114ED2B8} 36 | EndGlobalSection 37 | EndGlobal 38 | -------------------------------------------------------------------------------- /src/AzureFunctions.Extensions.CognitiveServices/AzureFunctions.Extensions.CognitiveServices.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.0 5 | Full 6 | true 7 | true 8 | 1.0.0-preview4 9 | Cognitive Services Extensions for Azure Functions 10 | https://github.com/joshdcar/azure-functions-extensions-cognitive-services 11 | git 12 | https://github.com/joshdcar/azure-functions-extensions-cognitive-services 13 | https://choosealicense.com/licenses/mit/ 14 | MIT License 15 | 16 | - Support for Azure Functions 2.0 GA 17 | - BREAKING CHANGE: Removed custom support for KeyVault as that functionality is now provided within the core Azure Functions framework. 18 | 19 | More details visit https://github.com/joshdcar/azure-functions-extensions-cognitive-services 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /src/AzureFunctions.Extensions.CognitiveServices/Bindings/Vision/Analysis/VisionAnalysisAttribute.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Azure.WebJobs.Description; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | 6 | namespace AzureFunctions.Extensions.CognitiveServices.Bindings.Vision.Analysis 7 | { 8 | [Binding] 9 | [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.ReturnValue)] 10 | public class VisionAnalysisAttribute : VisionAttributeBase 11 | { 12 | 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/AzureFunctions.Extensions.CognitiveServices/Bindings/Vision/Analysis/VisionAnalysisBinding.cs: -------------------------------------------------------------------------------- 1 | using AzureFunctions.Extensions.CognitiveServices.Config; 2 | using AzureFunctions.Extensions.CognitiveServices.Services; 3 | using Microsoft.Azure.WebJobs.Description; 4 | using Microsoft.Azure.WebJobs.Host.Config; 5 | using Microsoft.Extensions.Logging; 6 | using System; 7 | 8 | 9 | namespace AzureFunctions.Extensions.CognitiveServices.Bindings.Vision.Analysis 10 | { 11 | [Extension("VisionAnalysis")] 12 | public class VisionAnalysisBinding : IExtensionConfigProvider, IVisionBinding 13 | { 14 | 15 | public ICognitiveServicesClient Client {get;set;} 16 | 17 | internal ILoggerFactory _loggerFactory; 18 | 19 | public VisionAnalysisBinding(ILoggerFactory loggerFactory, ICognitiveServicesClient client) 20 | { 21 | _loggerFactory = loggerFactory; 22 | this.Client = client; 23 | } 24 | 25 | public void Initialize(ExtensionConfigContext context) 26 | { 27 | 28 | LoadClient(); 29 | 30 | var visionAnalysisRule = context.AddBindingRule(); 31 | 32 | visionAnalysisRule.When(nameof(VisionAnalysisAttribute.ImageSource), ImageSource.BlobStorage) 33 | .BindToInput(GetVisionAnalysisModel); 34 | 35 | visionAnalysisRule.When(nameof(VisionAnalysisAttribute.ImageSource), ImageSource.Url) 36 | .BindToInput(GetVisionAnalysisModel); 37 | 38 | visionAnalysisRule.When(nameof(VisionAnalysisAttribute.ImageSource), ImageSource.Client) 39 | .BindToInput(attr => new VisionAnalysisClient(this, attr, _loggerFactory)); 40 | 41 | } 42 | 43 | private void LoadClient() 44 | { 45 | if (Client == null) 46 | { 47 | Client = new CognitiveServicesClient(new RetryPolicy(), _loggerFactory); 48 | } 49 | } 50 | 51 | private VisionAnalysisModel GetVisionAnalysisModel(VisionAnalysisAttribute attribute) 52 | { 53 | 54 | if (attribute.ImageSource == Bindings.ImageSource.Client) 55 | { 56 | throw new ArgumentException($"ImageSource of Client does not support binding to vision models. Use Url or BlobStorage instead. "); 57 | } 58 | 59 | attribute.Validate(); 60 | 61 | var client = new VisionAnalysisClient(this, attribute, _loggerFactory); 62 | 63 | VisionAnalysisRequest request = new VisionAnalysisRequest(); 64 | 65 | if (attribute.ImageSource == ImageSource.BlobStorage) 66 | { 67 | var fileTask = StorageServices.GetFileBytes(attribute.BlobStoragePath, attribute.BlobStorageAccount); 68 | fileTask.Wait(); 69 | 70 | request.ImageBytes = fileTask.Result; 71 | 72 | } else 73 | { 74 | request.ImageUrl = attribute.ImageUrl; 75 | } 76 | 77 | var result = client.AnalyzeAsync(request); 78 | result.Wait(); 79 | 80 | return result.Result; 81 | 82 | } 83 | 84 | 85 | 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/AzureFunctions.Extensions.CognitiveServices/Bindings/Vision/Analysis/VisionAnalysisClient.cs: -------------------------------------------------------------------------------- 1 | using AzureFunctions.Extensions.CognitiveServices.Config; 2 | using AzureFunctions.Extensions.CognitiveServices.Services; 3 | using AzureFunctions.Extensions.CognitiveServices.Services.Models; 4 | using Microsoft.Extensions.Logging; 5 | using Newtonsoft.Json; 6 | using System; 7 | using System.Collections.Generic; 8 | using System.Diagnostics; 9 | using System.Net.Http; 10 | using System.Text; 11 | using System.Threading.Tasks; 12 | 13 | namespace AzureFunctions.Extensions.CognitiveServices.Bindings.Vision.Analysis 14 | { 15 | public class VisionAnalysisClient 16 | { 17 | IVisionBinding _config; 18 | VisionAnalysisAttribute _attr; 19 | ILogger _log; 20 | 21 | public VisionAnalysisClient(IVisionBinding config, VisionAnalysisAttribute attr, ILoggerFactory loggerFactory) 22 | { 23 | this._config = config; 24 | this._attr = attr; 25 | this._log = loggerFactory?.CreateLogger("Host.Bindings.VisionAnalysis"); 26 | } 27 | 28 | public async Task AnalyzeAsync(VisionAnalysisRequest request) 29 | { 30 | Stopwatch imageResizeSW = null; 31 | 32 | var visionOperation = await MergeProperties(request, this._config, this._attr); 33 | 34 | if (request.IsUrlImageSource == false) 35 | { 36 | 37 | if (visionOperation.ImageBytes == null || visionOperation.ImageBytes.Length == 0) 38 | { 39 | _log.LogWarning(VisionExceptionMessages.FileMissing); 40 | throw new ArgumentException(VisionExceptionMessages.FileMissing); 41 | } 42 | 43 | if (ImageResizeService.IsImage(visionOperation.ImageBytes) == false) 44 | { 45 | _log.LogWarning(VisionExceptionMessages.InvalidFileType); 46 | throw new ArgumentException(VisionExceptionMessages.InvalidFileType); 47 | } 48 | 49 | if (visionOperation.Oversized == true && visionOperation.AutoResize == false) 50 | { 51 | var message = string.Format(VisionExceptionMessages.FileTooLarge, 52 | VisionConfiguration.MaximumFileSize, visionOperation.ImageBytes.Length); 53 | _log.LogWarning(message); 54 | throw new ArgumentException(message); 55 | } 56 | else if (visionOperation.Oversized == true && visionOperation.AutoResize == true) 57 | { 58 | _log.LogTrace("Resizing Image"); 59 | 60 | imageResizeSW = new Stopwatch(); 61 | 62 | imageResizeSW.Start(); 63 | 64 | visionOperation.ImageBytes = ImageResizeService.ResizeImage(visionOperation.ImageBytes); 65 | 66 | imageResizeSW.Stop(); 67 | 68 | _log.LogMetric("VisionAnalysisImageResizeDurationMillisecond", imageResizeSW.ElapsedMilliseconds); 69 | 70 | if (visionOperation.Oversized) 71 | { 72 | var message = string.Format(VisionExceptionMessages.FileTooLargeAfterResize, 73 | VisionConfiguration.MaximumFileSize, visionOperation.ImageBytes.Length); 74 | _log.LogWarning(message); 75 | throw new ArgumentException(message); 76 | } 77 | 78 | } 79 | } 80 | 81 | var result = await SubmitRequest(visionOperation); 82 | 83 | return result; 84 | } 85 | 86 | private async Task SubmitRequest(VisionAnalysisRequest request) 87 | { 88 | Stopwatch sw = new Stopwatch(); 89 | 90 | string requestParameters = GetVisionOperationParameters(request); 91 | 92 | string uri = $"{request.Url}/analyze?{requestParameters}"; 93 | 94 | ServiceResultModel requestResult = null; 95 | 96 | if (request.IsUrlImageSource) 97 | { 98 | _log.LogTrace($"Submitting Vision Analysis Request"); 99 | 100 | var urlRequest = new VisionUrlRequest { Url = request.ImageUrl }; 101 | var requestContent = JsonConvert.SerializeObject(urlRequest); 102 | 103 | var content = new StringContent(requestContent); 104 | 105 | sw.Start(); 106 | 107 | requestResult = await this._config.Client.PostAsync(uri, request.Key, content, ReturnType.String); 108 | 109 | sw.Stop(); 110 | 111 | _log.LogMetric("VisionRequestDurationMillisecond", sw.ElapsedMilliseconds); 112 | 113 | } 114 | else 115 | { 116 | using (ByteArrayContent content = new ByteArrayContent(request.ImageBytes)) 117 | { 118 | requestResult = await this._config.Client.PostAsync(uri, request.Key, content, ReturnType.String); 119 | } 120 | } 121 | 122 | if (requestResult.HttpStatusCode == (int)System.Net.HttpStatusCode.OK) 123 | { 124 | _log.LogTrace($"Analysis Request Results: {requestResult.Contents}"); 125 | 126 | VisionAnalysisModel result = JsonConvert.DeserializeObject(requestResult.Contents); 127 | 128 | return result; 129 | } 130 | else if (requestResult.HttpStatusCode == (int)System.Net.HttpStatusCode.BadRequest) 131 | { 132 | 133 | VisionErrorModel error = JsonConvert.DeserializeObject(requestResult.Contents); 134 | var message = string.Format(VisionExceptionMessages.CognitiveServicesException, error.Code, error.Message); 135 | 136 | _log.LogWarning(message); 137 | 138 | throw new Exception(message); 139 | } 140 | else 141 | { 142 | var message = string.Format(VisionExceptionMessages.CognitiveServicesException, requestResult.HttpStatusCode, requestResult.Contents); 143 | 144 | _log.LogError(message); 145 | 146 | throw new Exception(message); 147 | } 148 | 149 | } 150 | 151 | private string GetVisionOperationParameters(VisionAnalysisRequest request) 152 | { 153 | VisionAnalysisOptions options = request.Options; 154 | 155 | string optionsParam = string.Empty; 156 | string visualFeaturesParam = string.Empty; 157 | string detailsParam = string.Empty; 158 | string languageParam = "&language=en"; 159 | 160 | if (options == VisionAnalysisOptions.All) 161 | { 162 | options = VisionAnalysisOptions.Categories | 163 | VisionAnalysisOptions.Celebrities | 164 | VisionAnalysisOptions.Color | 165 | VisionAnalysisOptions.Description | 166 | VisionAnalysisOptions.Faces | 167 | VisionAnalysisOptions.ImageType | 168 | VisionAnalysisOptions.Landmarks | 169 | VisionAnalysisOptions.Tags; 170 | } 171 | 172 | 173 | //Details Parameters 174 | if (options.HasFlag(VisionAnalysisOptions.Celebrities) 175 | || options.HasFlag(VisionAnalysisOptions.Landmarks)) 176 | { 177 | 178 | List details = new List(); 179 | 180 | if (options.HasFlag(VisionAnalysisOptions.Celebrities)) 181 | { 182 | details.Add("Celebrities"); 183 | } 184 | 185 | if (options.HasFlag(VisionAnalysisOptions.Landmarks)) 186 | { 187 | details.Add("Landmarks"); 188 | } 189 | 190 | detailsParam = $"&details={string.Join(",", details)}"; 191 | 192 | //Remove the Details Flags from the options so they are not 193 | //included in subsequent operations 194 | options = options & ~(VisionAnalysisOptions.Celebrities | VisionAnalysisOptions.Landmarks); 195 | 196 | } 197 | 198 | //Visual Features Parameter 199 | visualFeaturesParam = options.ToString(); 200 | visualFeaturesParam = visualFeaturesParam.Replace(" ", string.Empty); 201 | visualFeaturesParam = $"visualFeatures={visualFeaturesParam}"; 202 | 203 | //Combine All parameter 204 | optionsParam = $"{visualFeaturesParam}{detailsParam}{languageParam}"; 205 | 206 | return optionsParam; 207 | } 208 | 209 | private async Task MergeProperties(VisionAnalysisRequest operation, IVisionBinding config, VisionAnalysisAttribute attr) 210 | { 211 | 212 | var visionOperation = new VisionAnalysisRequest 213 | { 214 | Url = attr.VisionUrl ?? operation.Url, 215 | Key = attr.VisionKey ?? operation.Key, 216 | AutoResize = attr.AutoResize, 217 | Options = operation.Options, 218 | ImageUrl = string.IsNullOrEmpty(operation.ImageUrl) ? attr.ImageUrl : operation.ImageUrl, 219 | ImageBytes = operation.ImageBytes, 220 | }; 221 | 222 | if(string.IsNullOrEmpty(visionOperation.Key)) 223 | { 224 | _log.LogWarning(VisionExceptionMessages.KeyMissing); 225 | throw new ArgumentException(VisionExceptionMessages.KeyMissing); 226 | } 227 | 228 | return visionOperation; 229 | 230 | } 231 | 232 | } 233 | 234 | } 235 | -------------------------------------------------------------------------------- /src/AzureFunctions.Extensions.CognitiveServices/Bindings/Vision/Analysis/VisionAnalysisExtension.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Azure.WebJobs; 2 | using System; 3 | 4 | 5 | namespace AzureFunctions.Extensions.CognitiveServices.Bindings.Vision.Analysis 6 | { 7 | public static class VisionAnalysisExtension 8 | { 9 | public static IWebJobsBuilder AddVisionAnalysis(this IWebJobsBuilder builder) 10 | { 11 | if (builder == null) 12 | { 13 | throw new ArgumentNullException(nameof(builder)); 14 | } 15 | 16 | builder.AddExtension(); 17 | return builder; 18 | } 19 | 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /src/AzureFunctions.Extensions.CognitiveServices/Bindings/Vision/Analysis/VisionAnalysisModel.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | 6 | namespace AzureFunctions.Extensions.CognitiveServices.Bindings.Vision.Analysis 7 | { 8 | public class VisionAnalysisModel 9 | { 10 | [JsonProperty(PropertyName = "categories")] 11 | public IEnumerable Categories { get; set; } 12 | 13 | [JsonProperty(PropertyName = "tags")] 14 | public IEnumerable Tags { get; set; } 15 | 16 | [JsonProperty(PropertyName = "description")] 17 | public VisionDescription Description { get; set; } 18 | 19 | [JsonProperty(PropertyName = "faces")] 20 | public IEnumerable Faces { get; set; } 21 | 22 | [JsonProperty(PropertyName = "color")] 23 | public VisionColor Color { get; set; } 24 | 25 | [JsonProperty(PropertyName = "imageType")] 26 | public VisionImageType ImageType { get; set; } 27 | 28 | [JsonProperty(PropertyName = "metadata")] 29 | public VisionMetadata Metadata { get; set; } 30 | 31 | public override string ToString() 32 | { 33 | return JsonConvert.SerializeObject(this); 34 | } 35 | 36 | } 37 | 38 | public class VisionCategory 39 | { 40 | [JsonProperty(PropertyName = "name")] 41 | public string Name { get; set; } 42 | 43 | [JsonProperty(PropertyName = "score")] 44 | public double Score { get; set; } 45 | 46 | [JsonProperty(PropertyName = "detail")] 47 | public VisionDetail Detail { get; set; } 48 | } 49 | 50 | public class VisionDetail 51 | { 52 | [JsonProperty(PropertyName = "landmarks")] 53 | public IEnumerable Landmarks { get; set; } 54 | 55 | [JsonProperty(PropertyName = "celebrities")] 56 | public IEnumerable Celebrities { get; set; } 57 | } 58 | 59 | public class VisionLandmark 60 | { 61 | [JsonProperty(PropertyName = "name")] 62 | public string Name { get; set; } 63 | 64 | [JsonProperty(PropertyName = "confidence")] 65 | public double Confidence { get; set; } 66 | } 67 | 68 | public class VisionCelebrity 69 | { 70 | [JsonProperty(PropertyName = "name")] 71 | public string Name { get; set; } 72 | 73 | [JsonProperty(PropertyName = "confidence")] 74 | public double Confidence { get; set; } 75 | 76 | [JsonProperty(PropertyName = "faceRectangle")] 77 | public VisionFaceRectangle FaceRectangle { get; set; } 78 | } 79 | 80 | public class VisionTag 81 | { 82 | [JsonProperty(PropertyName = "name")] 83 | public string Name { get; set; } 84 | 85 | [JsonProperty(PropertyName = "confidence")] 86 | public double Confidence { get; set; } 87 | } 88 | 89 | public class VisionDescription 90 | { 91 | [JsonProperty(PropertyName = "tags")] 92 | public IEnumerable Tags { get; set; } 93 | 94 | [JsonProperty(PropertyName = "captions")] 95 | public IEnumerable Captions { get; set; } 96 | 97 | } 98 | 99 | public class VisionCaption 100 | { 101 | [JsonProperty(PropertyName = "text")] 102 | public string Text { get; set; } 103 | 104 | [JsonProperty(PropertyName = "confidence")] 105 | public double Confidence { get; set; } 106 | } 107 | 108 | public class VisionFace 109 | { 110 | [JsonProperty(PropertyName = "age")] 111 | public int Age { get; set; } 112 | 113 | [JsonProperty(PropertyName = "gender")] 114 | public string Gender { get; set; } 115 | 116 | [JsonProperty(PropertyName = "faceRectangle")] 117 | public VisionFaceRectangle FaceRectangle { get; set; } 118 | } 119 | 120 | public class VisionFaceRectangle 121 | { 122 | [JsonProperty(PropertyName = "top")] 123 | public int Top { get; set; } 124 | 125 | [JsonProperty(PropertyName = "left")] 126 | public int Left { get; set; } 127 | 128 | [JsonProperty(PropertyName = "width")] 129 | public int Width { get; set; } 130 | 131 | [JsonProperty(PropertyName = "length")] 132 | public int Length { get; set; } 133 | } 134 | 135 | public class VisionColor 136 | { 137 | [JsonProperty(PropertyName = "dominantColorForeground")] 138 | public string DominantColorForeground { get; set; } 139 | 140 | [JsonProperty(PropertyName = "dominantColorBackground")] 141 | public string DominantColorBackground { get; set; } 142 | 143 | [JsonProperty(PropertyName = "dominantColors")] 144 | public IEnumerable DominantColors { get; set; } 145 | 146 | [JsonProperty(PropertyName = "accentColor")] 147 | public string AccentColor { get; set; } 148 | 149 | [JsonProperty(PropertyName = "isBwImg")] 150 | public bool IsBwImg { get; set; } 151 | } 152 | 153 | public class VisionImageType 154 | { 155 | [JsonProperty(PropertyName = "clipArtType")] 156 | public int ClipArtType { get; set; } 157 | 158 | [JsonProperty(PropertyName = "lineDrawingType")] 159 | public int LineDrawingType { get; set; } 160 | } 161 | 162 | public class VisionMetadata 163 | { 164 | [JsonProperty(PropertyName = "height")] 165 | public int Height { get; set; } 166 | 167 | [JsonProperty(PropertyName = "width")] 168 | public int Width { get; set; } 169 | 170 | [JsonProperty(PropertyName = "format")] 171 | public string Format { get; set; } 172 | } 173 | } 174 | -------------------------------------------------------------------------------- /src/AzureFunctions.Extensions.CognitiveServices/Bindings/Vision/Analysis/VisionAnalysisRequest.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.IO; 5 | using System.Text; 6 | 7 | namespace AzureFunctions.Extensions.CognitiveServices.Bindings.Vision.Analysis 8 | { 9 | [Flags] 10 | public enum VisionAnalysisOptions 11 | { 12 | All = 0, 13 | Categories = 1, 14 | Tags = 2, 15 | Description = 4, 16 | Faces = 8, 17 | ImageType = 16, 18 | Color = 32, 19 | Adult = 64, 20 | Celebrities = 128, 21 | Landmarks = 256 22 | } 23 | 24 | public class VisionAnalysisRequest : VisionRequestBase 25 | { 26 | public VisionAnalysisRequest() { } 27 | 28 | public VisionAnalysisRequest(Stream image) : base(image) { } 29 | 30 | public VisionAnalysisRequest(byte[] image) : base(image) { } 31 | 32 | public VisionAnalysisRequest(string imageUrl) : base(imageUrl) { } 33 | 34 | 35 | [JsonProperty("options")] 36 | public VisionAnalysisOptions Options { get; set; } = VisionAnalysisOptions.All; 37 | 38 | 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/AzureFunctions.Extensions.CognitiveServices/Bindings/Vision/Analysis/VisionAnalysisStartup.cs: -------------------------------------------------------------------------------- 1 | using AzureFunctions.Extensions.CognitiveServices.Bindings.Vision.Analysis; 2 | using Microsoft.Azure.WebJobs; 3 | using Microsoft.Azure.WebJobs.Hosting; 4 | 5 | [assembly: WebJobsStartup(typeof(VisionAnalysisWebJobsStartup))] 6 | 7 | namespace AzureFunctions.Extensions.CognitiveServices.Bindings.Vision.Analysis 8 | { 9 | public class VisionAnalysisWebJobsStartup : IWebJobsStartup 10 | { 11 | public void Configure(IWebJobsBuilder builder) 12 | { 13 | builder.AddVisionAnalysis(); 14 | } 15 | } 16 | 17 | 18 | } -------------------------------------------------------------------------------- /src/AzureFunctions.Extensions.CognitiveServices/Bindings/Vision/Describe/VisionDescribeAttribute.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Azure.WebJobs.Description; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | 6 | namespace AzureFunctions.Extensions.CognitiveServices.Bindings.Vision.Describe 7 | { 8 | [Binding] 9 | [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.ReturnValue)] 10 | public class VisionDescribeAttribute : VisionAttributeBase 11 | { 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/AzureFunctions.Extensions.CognitiveServices/Bindings/Vision/Describe/VisionDescribeBinding.cs: -------------------------------------------------------------------------------- 1 | using AzureFunctions.Extensions.CognitiveServices.Config; 2 | using AzureFunctions.Extensions.CognitiveServices.Services; 3 | using Microsoft.Azure.WebJobs.Description; 4 | using Microsoft.Azure.WebJobs.Host.Config; 5 | using Microsoft.Extensions.Logging; 6 | using System; 7 | using System.Collections.Generic; 8 | using System.Text; 9 | 10 | namespace AzureFunctions.Extensions.CognitiveServices.Bindings.Vision.Describe 11 | { 12 | 13 | [Extension("VisionDescribe")] 14 | public class VisionDescribeBinding : IExtensionConfigProvider, IVisionBinding 15 | { 16 | 17 | public ICognitiveServicesClient Client { get; set; } 18 | 19 | internal ILoggerFactory _loggerFactory; 20 | 21 | public VisionDescribeBinding(ILoggerFactory loggerFactory, ICognitiveServicesClient client) 22 | { 23 | _loggerFactory = loggerFactory; 24 | this.Client = client; 25 | } 26 | 27 | public void Initialize(ExtensionConfigContext context) 28 | { 29 | 30 | LoadClient(); 31 | 32 | var visionDescribeRule = context.AddBindingRule(); 33 | 34 | visionDescribeRule.When(nameof(VisionDescribeAttribute.ImageSource), ImageSource.BlobStorage) 35 | .BindToInput(GetVisionDescribeModel); 36 | 37 | visionDescribeRule.When(nameof(VisionDescribeAttribute.ImageSource), ImageSource.Url) 38 | .BindToInput(GetVisionDescribeModel); 39 | 40 | visionDescribeRule.When(nameof(VisionDescribeAttribute.ImageSource), ImageSource.Client) 41 | .BindToInput(attr => new VisionDescribeClient(this, attr, _loggerFactory)); 42 | 43 | 44 | } 45 | 46 | private void LoadClient() 47 | { 48 | if (Client == null) 49 | { 50 | Client = new CognitiveServicesClient(new RetryPolicy(), _loggerFactory); 51 | } 52 | } 53 | 54 | private VisionDescribeModel GetVisionDescribeModel(VisionDescribeAttribute attribute) 55 | { 56 | 57 | if (attribute.ImageSource == Bindings.ImageSource.Client) 58 | { 59 | throw new ArgumentException($"ImageSource of Client does not support binding to vision models. Use Url or BlobStorage instead. "); 60 | } 61 | 62 | attribute.Validate(); 63 | 64 | var client = new VisionDescribeClient(this, attribute, _loggerFactory); 65 | 66 | VisionDescribeRequest request = new VisionDescribeRequest(); 67 | 68 | if (attribute.ImageSource == ImageSource.BlobStorage) 69 | { 70 | var fileTask = StorageServices.GetFileBytes(attribute.BlobStoragePath, attribute.BlobStorageAccount); 71 | fileTask.Wait(); 72 | 73 | request.ImageBytes = fileTask.Result; 74 | } 75 | else 76 | { 77 | request.ImageUrl = attribute.ImageUrl; 78 | } 79 | 80 | var result = client.DescribeAsync(request); 81 | result.Wait(); 82 | 83 | return result.Result; 84 | 85 | } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/AzureFunctions.Extensions.CognitiveServices/Bindings/Vision/Describe/VisionDescribeClient.cs: -------------------------------------------------------------------------------- 1 | using AzureFunctions.Extensions.CognitiveServices.Config; 2 | using AzureFunctions.Extensions.CognitiveServices.Services; 3 | using AzureFunctions.Extensions.CognitiveServices.Services.Models; 4 | using Microsoft.Extensions.Logging; 5 | using Newtonsoft.Json; 6 | using System; 7 | using System.Collections.Generic; 8 | using System.Diagnostics; 9 | using System.Net.Http; 10 | using System.Text; 11 | using System.Threading.Tasks; 12 | 13 | namespace AzureFunctions.Extensions.CognitiveServices.Bindings.Vision.Describe 14 | { 15 | public class VisionDescribeClient 16 | { 17 | IVisionBinding _config; 18 | VisionDescribeAttribute _attr; 19 | ILogger _log; 20 | 21 | public VisionDescribeClient(IVisionBinding config, VisionDescribeAttribute attr, ILoggerFactory loggerFactory) 22 | { 23 | this._config = config; 24 | this._attr = attr; 25 | this._log = loggerFactory?.CreateLogger("Host.Bindings.VisionDescribe"); 26 | } 27 | 28 | public async Task DescribeAsync(VisionDescribeRequest request) 29 | { 30 | try 31 | { 32 | Stopwatch imageResizeSW = null; 33 | 34 | var visionOperation = await MergeProperties(request, this._config, this._attr); 35 | 36 | if (request.IsUrlImageSource == false) 37 | { 38 | 39 | if (visionOperation.ImageBytes == null || visionOperation.ImageBytes.Length == 0) 40 | { 41 | _log.LogWarning(VisionExceptionMessages.FileMissing); 42 | throw new ArgumentException(VisionExceptionMessages.FileMissing); 43 | } 44 | 45 | if (ImageResizeService.IsImage(visionOperation.ImageBytes) == false) 46 | { 47 | _log.LogWarning(VisionExceptionMessages.InvalidFileType); 48 | throw new ArgumentException(VisionExceptionMessages.InvalidFileType); 49 | } 50 | 51 | if (visionOperation.Oversized == true && visionOperation.AutoResize == false) 52 | { 53 | var message = string.Format(VisionExceptionMessages.FileTooLarge, 54 | VisionConfiguration.MaximumFileSize, visionOperation.ImageBytes.Length); 55 | _log.LogWarning(message); 56 | throw new ArgumentException(message); 57 | } 58 | else if (visionOperation.Oversized == true && visionOperation.AutoResize == true) 59 | { 60 | _log.LogTrace("Resizing Image"); 61 | 62 | imageResizeSW = new Stopwatch(); 63 | 64 | imageResizeSW.Start(); 65 | 66 | visionOperation.ImageBytes = ImageResizeService.ResizeImage(visionOperation.ImageBytes); 67 | 68 | imageResizeSW.Stop(); 69 | 70 | _log.LogMetric("VisionAnalysisImageResizeDurationMillisecond", imageResizeSW.ElapsedMilliseconds); 71 | 72 | if (visionOperation.Oversized) 73 | { 74 | var message = string.Format(VisionExceptionMessages.FileTooLargeAfterResize, 75 | VisionConfiguration.MaximumFileSize, visionOperation.ImageBytes.Length); 76 | _log.LogWarning(message); 77 | throw new ArgumentException(message); 78 | } 79 | 80 | } 81 | } 82 | 83 | var result = await SubmitRequest(visionOperation); 84 | 85 | return result; 86 | } 87 | catch(Exception ex ) 88 | { 89 | throw ex; 90 | } 91 | } 92 | 93 | private async Task SubmitRequest(VisionDescribeRequest request) 94 | { 95 | Stopwatch sw = new Stopwatch(); 96 | 97 | string uri = $"{request.Url}/describe?maxCandidates={request.MaxCandidates}"; 98 | 99 | ServiceResultModel requestResult = null; 100 | 101 | if (request.IsUrlImageSource) 102 | { 103 | _log.LogTrace($"Submitting Vision Describe Request"); 104 | 105 | var urlRequest = new VisionUrlRequest { Url = request.ImageUrl }; 106 | var requestContent = JsonConvert.SerializeObject(urlRequest); 107 | 108 | var content = new StringContent(requestContent); 109 | 110 | sw.Start(); 111 | 112 | requestResult = await this._config.Client.PostAsync(uri, request.Key, content, ReturnType.String); 113 | 114 | sw.Stop(); 115 | 116 | _log.LogMetric("VisionRequestDurationMillisecond", sw.ElapsedMilliseconds); 117 | 118 | } 119 | else 120 | { 121 | using (ByteArrayContent content = new ByteArrayContent(request.ImageBytes)) 122 | { 123 | requestResult = await this._config.Client.PostAsync(uri, request.Key, content, ReturnType.String); 124 | } 125 | } 126 | 127 | if (requestResult.HttpStatusCode == (int)System.Net.HttpStatusCode.OK) 128 | { 129 | _log.LogTrace($"Describe Request Results: {requestResult.Contents}"); 130 | 131 | VisionDescribeModel result = JsonConvert.DeserializeObject(requestResult.Contents); 132 | 133 | return result; 134 | } 135 | else if (requestResult.HttpStatusCode == (int)System.Net.HttpStatusCode.BadRequest) 136 | { 137 | 138 | VisionErrorModel error = JsonConvert.DeserializeObject(requestResult.Contents); 139 | var message = string.Format(VisionExceptionMessages.CognitiveServicesException, error.Code, error.Message); 140 | 141 | _log.LogWarning(message); 142 | 143 | throw new Exception(message); 144 | } 145 | else 146 | { 147 | var message = string.Format(VisionExceptionMessages.CognitiveServicesException, requestResult.HttpStatusCode, requestResult.Contents); 148 | 149 | _log.LogError(message); 150 | 151 | throw new Exception(message); 152 | } 153 | 154 | } 155 | 156 | private async Task MergeProperties(VisionDescribeRequest operation, IVisionBinding config, VisionDescribeAttribute attr) 157 | { 158 | 159 | var visionOperation = new VisionDescribeRequest 160 | { 161 | Url = attr.VisionUrl ?? operation.Url, 162 | Key = attr.VisionKey ?? operation.Key, 163 | AutoResize = attr.AutoResize, 164 | MaxCandidates = operation.MaxCandidates, 165 | ImageUrl = string.IsNullOrEmpty(operation.ImageUrl) ? attr.ImageUrl : operation.ImageUrl, 166 | ImageBytes = operation.ImageBytes, 167 | }; 168 | 169 | 170 | if (string.IsNullOrEmpty(visionOperation.Key)) 171 | { 172 | _log.LogWarning(VisionExceptionMessages.KeyMissing); 173 | throw new ArgumentException(VisionExceptionMessages.KeyMissing); 174 | } 175 | 176 | 177 | return visionOperation; 178 | 179 | } 180 | 181 | } 182 | } 183 | -------------------------------------------------------------------------------- /src/AzureFunctions.Extensions.CognitiveServices/Bindings/Vision/Describe/VisionDescribeExtension.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Azure.WebJobs; 2 | using System; 3 | 4 | namespace AzureFunctions.Extensions.CognitiveServices.Bindings.Vision.Describe 5 | { 6 | public static class VisionDescribeExtension 7 | { 8 | public static IWebJobsBuilder AddVisionDescribe(this IWebJobsBuilder builder) 9 | { 10 | if (builder == null) 11 | { 12 | throw new ArgumentNullException(nameof(builder)); 13 | } 14 | 15 | builder.AddExtension(); 16 | return builder; 17 | } 18 | 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /src/AzureFunctions.Extensions.CognitiveServices/Bindings/Vision/Describe/VisionDescribeModel.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | 6 | namespace AzureFunctions.Extensions.CognitiveServices.Bindings.Vision.Describe 7 | { 8 | public class VisionDescribeModel 9 | { 10 | 11 | [JsonProperty(PropertyName = "description")] 12 | public VisionDescribeDescription Description { get; set; } 13 | 14 | [JsonProperty(PropertyName = "metadata")] 15 | public VisionDescribeMetadata Metadata { get; set; } 16 | 17 | public override string ToString() 18 | { 19 | return JsonConvert.SerializeObject(this); 20 | } 21 | 22 | } 23 | 24 | public class VisionDescribeDescription 25 | { 26 | [JsonProperty(PropertyName = "tags")] 27 | public IEnumerable Tags { get; set; } 28 | 29 | [JsonProperty(PropertyName = "captions")] 30 | public IEnumerable Captions { get; set; } 31 | } 32 | 33 | public class VisionDescribeCaption 34 | { 35 | [JsonProperty(PropertyName = "text")] 36 | public string Text { get; set; } 37 | 38 | [JsonProperty(PropertyName = "confidence")] 39 | public double Confidence { get; set; } 40 | } 41 | 42 | public class VisionDescribeMetadata 43 | { 44 | [JsonProperty(PropertyName = "height")] 45 | public int Height { get; set; } 46 | 47 | [JsonProperty(PropertyName = "width")] 48 | public int Width { get; set; } 49 | 50 | [JsonProperty(PropertyName = "format")] 51 | public string Format { get; set; } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/AzureFunctions.Extensions.CognitiveServices/Bindings/Vision/Describe/VisionDescribeRequest.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.IO; 5 | using System.Text; 6 | 7 | namespace AzureFunctions.Extensions.CognitiveServices.Bindings.Vision.Describe 8 | { 9 | public class VisionDescribeRequest : VisionRequestBase 10 | { 11 | public VisionDescribeRequest() { } 12 | 13 | public VisionDescribeRequest(Stream image) : base(image) { } 14 | 15 | public VisionDescribeRequest(byte[] image) : base(image) { } 16 | 17 | public VisionDescribeRequest(string imageUrl) : base(imageUrl) { } 18 | 19 | [JsonProperty("maxCandidates")] 20 | public int MaxCandidates { get; set; } = 1; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/AzureFunctions.Extensions.CognitiveServices/Bindings/Vision/Describe/VisionDescribeStartup.cs: -------------------------------------------------------------------------------- 1 | using AzureFunctions.Extensions.CognitiveServices.Bindings.Vision.Analysis; 2 | using AzureFunctions.Extensions.CognitiveServices.Bindings.Vision.Describe; 3 | using Microsoft.Azure.WebJobs; 4 | using Microsoft.Azure.WebJobs.Hosting; 5 | 6 | [assembly: WebJobsStartup(typeof(VisionDescribeWebJobsStartup))] 7 | 8 | namespace AzureFunctions.Extensions.CognitiveServices.Bindings.Vision.Describe 9 | { 10 | 11 | public class VisionDescribeWebJobsStartup : IWebJobsStartup 12 | { 13 | public void Configure(IWebJobsBuilder builder) 14 | { 15 | builder.AddVisionDescribe(); 16 | } 17 | } 18 | 19 | 20 | } -------------------------------------------------------------------------------- /src/AzureFunctions.Extensions.CognitiveServices/Bindings/Vision/Domain/VisionDomainAttribute.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Azure.WebJobs.Description; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | 6 | namespace AzureFunctions.Extensions.CognitiveServices.Bindings.Vision.Domain 7 | { 8 | 9 | [Binding] 10 | [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.ReturnValue)] 11 | public class VisionDomainAttribute : VisionAttributeBase 12 | { 13 | 14 | /// 15 | /// String representation of VisionDomainOptions so we can set 16 | /// options settings via the attribute which only supports strings 17 | /// 18 | public string Domain { get; set; } 19 | 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/AzureFunctions.Extensions.CognitiveServices/Bindings/Vision/Domain/VisionDomainBinding.cs: -------------------------------------------------------------------------------- 1 | using AzureFunctions.Extensions.CognitiveServices.Config; 2 | using AzureFunctions.Extensions.CognitiveServices.Services; 3 | using Microsoft.Azure.WebJobs.Description; 4 | using Microsoft.Azure.WebJobs.Host.Config; 5 | using Microsoft.Extensions.Logging; 6 | using System; 7 | using System.Collections.Generic; 8 | using System.Text; 9 | 10 | namespace AzureFunctions.Extensions.CognitiveServices.Bindings.Vision.Domain 11 | { 12 | 13 | [Extension("VisionDomain")] 14 | public class VisionDomainBinding : IExtensionConfigProvider, IVisionBinding 15 | { 16 | internal ILoggerFactory _loggerFactory; 17 | 18 | public VisionDomainBinding(ILoggerFactory loggerFactory, ICognitiveServicesClient client) 19 | { 20 | _loggerFactory = loggerFactory; 21 | this.Client = client; 22 | } 23 | 24 | public ICognitiveServicesClient Client { get; set; } 25 | 26 | public void Initialize(ExtensionConfigContext context) 27 | { 28 | 29 | LoadClient(); 30 | 31 | 32 | var visionDomainRule = context.AddBindingRule(); 33 | 34 | visionDomainRule.When(nameof(VisionDomainAttribute.ImageSource), ImageSource.BlobStorage) 35 | .BindToInput(GetVisionLandmarkModel); 36 | 37 | visionDomainRule.When(nameof(VisionDomainAttribute.ImageSource), ImageSource.Url) 38 | .BindToInput(GetVisionLandmarkModel); 39 | 40 | visionDomainRule.When(nameof(VisionDomainAttribute.ImageSource), ImageSource.BlobStorage) 41 | .BindToInput(GetVisionCelebrityModel); 42 | 43 | visionDomainRule.When(nameof(VisionDomainAttribute.ImageSource), ImageSource.Url) 44 | .BindToInput(GetVisionCelebrityModel); 45 | 46 | visionDomainRule.When(nameof(VisionDomainAttribute.ImageSource), ImageSource.Client) 47 | .BindToInput(attr => new VisionDomainClient(this, attr, _loggerFactory)); 48 | 49 | } 50 | 51 | private void LoadClient() 52 | { 53 | if (Client == null) 54 | { 55 | Client = new CognitiveServicesClient(new RetryPolicy(), _loggerFactory); 56 | } 57 | } 58 | 59 | private VisionDomainCelebrityModel GetVisionCelebrityModel(VisionDomainAttribute attribute) 60 | { 61 | 62 | if (attribute.ImageSource == Bindings.ImageSource.Client) 63 | { 64 | throw new ArgumentException($"ImageSource of Client does not support binding to vision models. Use Url or BlobStorage instead. "); 65 | } 66 | 67 | attribute.Validate(); 68 | 69 | var client = new VisionDomainClient(this, attribute, _loggerFactory); 70 | var request = BuildRequest(attribute); 71 | 72 | var result = client.AnalyzeCelebrityAsync(request); 73 | result.Wait(); 74 | 75 | return result.Result; 76 | 77 | } 78 | 79 | private VisionDomainLandmarkModel GetVisionLandmarkModel(VisionDomainAttribute attribute) 80 | { 81 | 82 | if (attribute.ImageSource == Bindings.ImageSource.Client) 83 | { 84 | throw new ArgumentException($"ImageSource of Client does not support binding to vision models. Use Url or BlobStorage instead. "); 85 | } 86 | 87 | attribute.Validate(); 88 | 89 | var client = new VisionDomainClient(this, attribute, _loggerFactory); 90 | var request = BuildRequest(attribute); 91 | 92 | var result = client.AnalyzeLandmarkAsync(request); 93 | result.Wait(); 94 | 95 | return result.Result; 96 | 97 | } 98 | 99 | private VisionDomainRequest BuildRequest(VisionDomainAttribute attribute) 100 | { 101 | VisionDomainRequest request = new VisionDomainRequest(); 102 | 103 | if (attribute.ImageSource == ImageSource.BlobStorage) 104 | { 105 | var fileTask = StorageServices.GetFileBytes(attribute.BlobStoragePath, attribute.BlobStorageAccount); 106 | fileTask.Wait(); 107 | 108 | request.ImageBytes = fileTask.Result; 109 | } 110 | else 111 | { 112 | request.ImageUrl = attribute.ImageUrl; 113 | } 114 | 115 | return request; 116 | 117 | } 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /src/AzureFunctions.Extensions.CognitiveServices/Bindings/Vision/Domain/VisionDomainCelebrityModel.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | 6 | namespace AzureFunctions.Extensions.CognitiveServices.Bindings.Vision.Domain 7 | { 8 | public class FaceRectangle 9 | { 10 | 11 | [JsonProperty("top")] 12 | public int Top { get; set; } 13 | 14 | [JsonProperty("left")] 15 | public int Left { get; set; } 16 | 17 | [JsonProperty("width")] 18 | public int Width { get; set; } 19 | 20 | [JsonProperty("height")] 21 | public int Height { get; set; } 22 | } 23 | 24 | public class Celebrity 25 | { 26 | 27 | [JsonProperty("faceRectangle")] 28 | public FaceRectangle FaceRectangle { get; set; } 29 | 30 | [JsonProperty("name")] 31 | public string Name { get; set; } 32 | 33 | [JsonProperty("confidence")] 34 | public double Confidence { get; set; } 35 | } 36 | 37 | public class CelebrityResult 38 | { 39 | 40 | [JsonProperty("celebrities")] 41 | public IList Celebrities { get; set; } 42 | } 43 | 44 | public class CelebrityMetadata 45 | { 46 | 47 | [JsonProperty("height")] 48 | public int Height { get; set; } 49 | 50 | [JsonProperty("width")] 51 | public int Width { get; set; } 52 | 53 | [JsonProperty("format")] 54 | public string Format { get; set; } 55 | } 56 | 57 | public class VisionDomainCelebrityModel 58 | { 59 | 60 | [JsonProperty("result")] 61 | public CelebrityResult Result { get; set; } 62 | 63 | [JsonProperty("requestId")] 64 | public string RequestId { get; set; } 65 | 66 | [JsonProperty("metadata")] 67 | public CelebrityMetadata Metadata { get; set; } 68 | 69 | public override string ToString() 70 | { 71 | return JsonConvert.SerializeObject(this); 72 | } 73 | } 74 | } 75 | 76 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /src/AzureFunctions.Extensions.CognitiveServices/Bindings/Vision/Domain/VisionDomainClient.cs: -------------------------------------------------------------------------------- 1 | using AzureFunctions.Extensions.CognitiveServices.Bindings.Vision.Analysis; 2 | using AzureFunctions.Extensions.CognitiveServices.Config; 3 | using AzureFunctions.Extensions.CognitiveServices.Services; 4 | using AzureFunctions.Extensions.CognitiveServices.Services.Models; 5 | using Microsoft.Extensions.Logging; 6 | using Newtonsoft.Json; 7 | using System; 8 | using System.Collections.Generic; 9 | using System.Diagnostics; 10 | using System.Net.Http; 11 | using System.Text; 12 | using System.Threading.Tasks; 13 | 14 | namespace AzureFunctions.Extensions.CognitiveServices.Bindings.Vision.Domain 15 | { 16 | public class VisionDomainClient 17 | { 18 | IVisionBinding _config; 19 | VisionDomainAttribute _attr; 20 | ILogger _log; 21 | 22 | public VisionDomainClient(IVisionBinding config, VisionDomainAttribute attr, ILoggerFactory loggerFactory) 23 | { 24 | this._config = config; 25 | this._attr = attr; 26 | this._log = loggerFactory?.CreateLogger("Host.Bindings.VisionDomain"); 27 | } 28 | 29 | public async Task AnalyzeCelebrityAsync(VisionDomainRequest request) 30 | { 31 | var result = await AnalyzeAsync(request); 32 | 33 | return result; 34 | } 35 | 36 | public async Task AnalyzeLandmarkAsync(VisionDomainRequest request) 37 | { 38 | var result = await AnalyzeAsync(request); 39 | 40 | return result; 41 | } 42 | 43 | private async Task AnalyzeAsync(VisionDomainRequest request) 44 | { 45 | Stopwatch imageResizeSW = null; 46 | 47 | var visionOperation = await MergeProperties(request, this._config, this._attr); 48 | 49 | if (request.IsUrlImageSource == false) 50 | { 51 | 52 | if (visionOperation.ImageBytes == null || visionOperation.ImageBytes.Length == 0) 53 | { 54 | _log.LogWarning(VisionExceptionMessages.FileMissing); 55 | throw new ArgumentException(VisionExceptionMessages.FileMissing); 56 | } 57 | 58 | if (ImageResizeService.IsImage(visionOperation.ImageBytes) == false) 59 | { 60 | _log.LogWarning(VisionExceptionMessages.InvalidFileType); 61 | throw new ArgumentException(VisionExceptionMessages.InvalidFileType); 62 | } 63 | 64 | if (visionOperation.Oversized == true && visionOperation.AutoResize == false) 65 | { 66 | var message = string.Format(VisionExceptionMessages.FileTooLarge, 67 | VisionConfiguration.MaximumFileSize, visionOperation.ImageBytes.Length); 68 | _log.LogWarning(message); 69 | throw new ArgumentException(message); 70 | } 71 | else if (visionOperation.Oversized == true && visionOperation.AutoResize == true) 72 | { 73 | _log.LogTrace("Resizing Image"); 74 | 75 | imageResizeSW = new Stopwatch(); 76 | 77 | imageResizeSW.Start(); 78 | 79 | visionOperation.ImageBytes = ImageResizeService.ResizeImage(visionOperation.ImageBytes); 80 | 81 | imageResizeSW.Stop(); 82 | 83 | _log.LogMetric("VisionAnalysisImageResizeDurationMillisecond", imageResizeSW.ElapsedMilliseconds); 84 | 85 | if (visionOperation.Oversized) 86 | { 87 | var message = string.Format(VisionExceptionMessages.FileTooLargeAfterResize, 88 | VisionConfiguration.MaximumFileSize, visionOperation.ImageBytes.Length); 89 | _log.LogWarning(message); 90 | throw new ArgumentException(message); 91 | } 92 | 93 | } 94 | } 95 | 96 | var result = await SubmitRequest(visionOperation); 97 | 98 | return result; 99 | } 100 | 101 | private async Task SubmitRequest(VisionDomainRequest request) 102 | { 103 | Stopwatch sw = new Stopwatch(); 104 | 105 | string requestParameters = GetVisionOperationParameters(request); 106 | 107 | string uri = $"{request.Url}/{requestParameters}"; 108 | 109 | ServiceResultModel requestResult = null; 110 | 111 | if (request.IsUrlImageSource) 112 | { 113 | _log.LogTrace($"Submitting Vision Domain Request"); 114 | 115 | var urlRequest = new VisionUrlRequest { Url = request.ImageUrl }; 116 | var requestContent = JsonConvert.SerializeObject(urlRequest); 117 | 118 | var content = new StringContent(requestContent); 119 | 120 | sw.Start(); 121 | 122 | requestResult = await this._config.Client.PostAsync(uri, request.Key, content, ReturnType.String); 123 | 124 | sw.Stop(); 125 | 126 | _log.LogMetric("VisionDomainRequestDurationMillisecond", sw.ElapsedMilliseconds); 127 | 128 | } 129 | else 130 | { 131 | using (ByteArrayContent content = new ByteArrayContent(request.ImageBytes)) 132 | { 133 | requestResult = await this._config.Client.PostAsync(uri, request.Key, content, ReturnType.String); 134 | } 135 | } 136 | 137 | if (requestResult.HttpStatusCode == (int)System.Net.HttpStatusCode.OK) 138 | { 139 | _log.LogTrace($"Analysis Request Results: {requestResult.Contents}"); 140 | 141 | var result = JsonConvert.DeserializeObject(requestResult.Contents); 142 | 143 | return result; 144 | } 145 | else if (requestResult.HttpStatusCode == (int)System.Net.HttpStatusCode.BadRequest) 146 | { 147 | 148 | VisionErrorModel error = JsonConvert.DeserializeObject(requestResult.Contents); 149 | var message = string.Format(VisionExceptionMessages.CognitiveServicesException, error.Code, error.Message); 150 | 151 | _log.LogWarning(message); 152 | 153 | throw new Exception(message); 154 | } 155 | else 156 | { 157 | var message = string.Format(VisionExceptionMessages.CognitiveServicesException, requestResult.HttpStatusCode, requestResult.Contents); 158 | 159 | _log.LogError(message); 160 | 161 | throw new Exception(message); 162 | } 163 | 164 | } 165 | 166 | private string GetVisionOperationParameters(VisionDomainRequest request) 167 | { 168 | 169 | string optionsParam = string.Empty; 170 | 171 | switch (request.Domain) 172 | { 173 | case VisionDomainOptions.Celebrity: 174 | optionsParam = "models/celebrities/analyze"; 175 | break; 176 | 177 | case VisionDomainOptions.Landmark: 178 | optionsParam = "models/landmarks/analyze "; 179 | break; 180 | } 181 | 182 | return optionsParam; 183 | } 184 | 185 | private async Task MergeProperties(VisionDomainRequest operation, IVisionBinding config, VisionDomainAttribute attr) 186 | { 187 | //Attributes do not allow for enum types so we have to validate 188 | //the string passed into the attribute to ensure it matches 189 | //a valid VisionDomainOption. 190 | VisionDomainOptions attrDomain = VisionDomainOptions.None; 191 | 192 | if (!string.IsNullOrEmpty(attr.Domain)) 193 | { 194 | var valid = Enum.TryParse(attr.Domain, out attrDomain); 195 | 196 | if (!valid) 197 | { 198 | var message = string.Format(VisionExceptionMessages.InvalidDomainName, attr.Domain); 199 | _log.LogWarning(message); 200 | 201 | throw new ArgumentException(message); 202 | } 203 | } 204 | else 205 | { 206 | if(operation.Domain == VisionDomainOptions.None) 207 | { 208 | var message = string.Format(VisionExceptionMessages.InvalidDomainName, "None"); 209 | _log.LogWarning(message); 210 | 211 | throw new ArgumentException(message); 212 | } 213 | } 214 | 215 | 216 | var visionOperation = new VisionDomainRequest 217 | { 218 | Url = attr.VisionUrl ?? operation.Url, 219 | Key = attr.VisionKey ?? operation.Key, 220 | AutoResize = attr.AutoResize, 221 | Domain = attrDomain == VisionDomainOptions.None ? operation.Domain : attrDomain, 222 | ImageUrl = string.IsNullOrEmpty(operation.ImageUrl) ? attr.ImageUrl : operation.ImageUrl, 223 | ImageBytes = operation.ImageBytes, 224 | }; 225 | 226 | if (string.IsNullOrEmpty(visionOperation.Key)) 227 | { 228 | _log.LogWarning(VisionExceptionMessages.KeyMissing); 229 | throw new ArgumentException(VisionExceptionMessages.KeyMissing); 230 | } 231 | 232 | return visionOperation; 233 | 234 | } 235 | 236 | } 237 | 238 | 239 | } 240 | -------------------------------------------------------------------------------- /src/AzureFunctions.Extensions.CognitiveServices/Bindings/Vision/Domain/VisionDomainExtension.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Azure.WebJobs; 2 | using System; 3 | 4 | namespace AzureFunctions.Extensions.CognitiveServices.Bindings.Vision.Domain 5 | { 6 | public static class VisionDomainExtension 7 | { 8 | public static IWebJobsBuilder AddVisionDomain(this IWebJobsBuilder builder) 9 | { 10 | if (builder == null) 11 | { 12 | throw new ArgumentNullException(nameof(builder)); 13 | } 14 | 15 | builder.AddExtension(); 16 | return builder; 17 | } 18 | 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /src/AzureFunctions.Extensions.CognitiveServices/Bindings/Vision/Domain/VisionDomainLandmarkModel.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | 6 | namespace AzureFunctions.Extensions.CognitiveServices.Bindings.Vision.Domain 7 | { 8 | public class Landmark 9 | { 10 | 11 | [JsonProperty("name")] 12 | public string Name { get; set; } 13 | 14 | [JsonProperty("confidence")] 15 | public double Confidence { get; set; } 16 | } 17 | 18 | public class Result 19 | { 20 | 21 | [JsonProperty("landmarks")] 22 | public IList Landmarks { get; set; } 23 | } 24 | 25 | public class LandmarkMetadata 26 | { 27 | 28 | [JsonProperty("height")] 29 | public int Height { get; set; } 30 | 31 | [JsonProperty("width")] 32 | public int Width { get; set; } 33 | 34 | [JsonProperty("format")] 35 | public string Format { get; set; } 36 | } 37 | 38 | public class VisionDomainLandmarkModel 39 | { 40 | 41 | [JsonProperty("result")] 42 | public Result Result { get; set; } 43 | 44 | [JsonProperty("requestId")] 45 | public string RequestId { get; set; } 46 | 47 | [JsonProperty("metadata")] 48 | public LandmarkMetadata Metadata { get; set; } 49 | 50 | public override string ToString() 51 | { 52 | return JsonConvert.SerializeObject(this); 53 | } 54 | } 55 | 56 | 57 | 58 | 59 | } 60 | -------------------------------------------------------------------------------- /src/AzureFunctions.Extensions.CognitiveServices/Bindings/Vision/Domain/VisionDomainRequest.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.IO; 5 | using System.Text; 6 | 7 | namespace AzureFunctions.Extensions.CognitiveServices.Bindings.Vision.Domain 8 | { 9 | public enum VisionDomainOptions 10 | { 11 | None=0, 12 | Landmark=1, 13 | Celebrity=2 14 | } 15 | 16 | public class VisionDomainRequest : VisionRequestBase 17 | { 18 | public const string CELEBRITY_DOMAIN = "Celebrity"; 19 | public const string LANDMARK_DOMAIN = "Landmark"; 20 | 21 | public VisionDomainRequest() { } 22 | 23 | public VisionDomainRequest(Stream image) : base(image) { } 24 | 25 | public VisionDomainRequest(byte[] image) : base(image) { } 26 | 27 | public VisionDomainRequest(string imageUrl) : base(imageUrl) { } 28 | 29 | 30 | [JsonProperty("options")] 31 | public VisionDomainOptions Domain { get; set; } = VisionDomainOptions.None; 32 | 33 | 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/AzureFunctions.Extensions.CognitiveServices/Bindings/Vision/Domain/VisionDomainStartup.cs: -------------------------------------------------------------------------------- 1 | using AzureFunctions.Extensions.CognitiveServices.Bindings.Vision.Analysis; 2 | using AzureFunctions.Extensions.CognitiveServices.Bindings.Vision.Domain; 3 | using Microsoft.Azure.WebJobs; 4 | using Microsoft.Azure.WebJobs.Hosting; 5 | 6 | [assembly: WebJobsStartup(typeof(VisionDomainWebJobsStartup))] 7 | 8 | namespace AzureFunctions.Extensions.CognitiveServices.Bindings.Vision.Domain 9 | { 10 | 11 | public class VisionDomainWebJobsStartup : IWebJobsStartup 12 | { 13 | public void Configure(IWebJobsBuilder builder) 14 | { 15 | builder.AddVisionDomain(); 16 | } 17 | } 18 | 19 | 20 | } 21 | -------------------------------------------------------------------------------- /src/AzureFunctions.Extensions.CognitiveServices/Bindings/Vision/Handwriting/VisionHandwritingAttribute.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Azure.WebJobs.Description; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | 6 | namespace AzureFunctions.Extensions.CognitiveServices.Bindings.Vision.Handwriting 7 | { 8 | [Binding] 9 | [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.ReturnValue)] 10 | 11 | public class VisionHandwritingAttribute : VisionAttributeBase 12 | { 13 | 14 | public bool? Handwriting { get; set; } = true; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/AzureFunctions.Extensions.CognitiveServices/Bindings/Vision/Handwriting/VisionHandwritingBinding.cs: -------------------------------------------------------------------------------- 1 | using AzureFunctions.Extensions.CognitiveServices.Config; 2 | using AzureFunctions.Extensions.CognitiveServices.Services; 3 | using Microsoft.Azure.WebJobs.Description; 4 | using Microsoft.Azure.WebJobs.Host.Config; 5 | using Microsoft.Extensions.Logging; 6 | using System; 7 | using System.Collections.Generic; 8 | using System.Text; 9 | 10 | namespace AzureFunctions.Extensions.CognitiveServices.Bindings.Vision.Handwriting 11 | { 12 | [Extension("VisionHandwriting")] 13 | public class VisionHandwritingBinding : IExtensionConfigProvider, IVisionBinding 14 | { 15 | 16 | public ICognitiveServicesClient Client { get; set; } 17 | 18 | internal ILoggerFactory _loggerFactory; 19 | 20 | public VisionHandwritingBinding(ILoggerFactory factory, ICognitiveServicesClient client) 21 | { 22 | _loggerFactory = factory; 23 | this.Client = client; 24 | } 25 | 26 | public void Initialize(ExtensionConfigContext context) 27 | { 28 | 29 | LoadClient(); 30 | 31 | var visionRule = context.AddBindingRule(); 32 | 33 | visionRule.When(nameof(VisionHandwritingAttribute.ImageSource), ImageSource.BlobStorage) 34 | .BindToInput(GetVisionHandwritingModel); 35 | 36 | visionRule.When(nameof(VisionHandwritingAttribute.ImageSource), ImageSource.Url) 37 | .BindToInput(GetVisionHandwritingModel); 38 | 39 | visionRule.When(nameof(VisionHandwritingAttribute.ImageSource), ImageSource.Client) 40 | .BindToInput(attr => new VisionHandwritingClient(this, attr, _loggerFactory)); 41 | 42 | } 43 | 44 | private void LoadClient() 45 | { 46 | if (Client == null) 47 | { 48 | Client = new CognitiveServicesClient(new RetryPolicy(), _loggerFactory); 49 | } 50 | } 51 | 52 | private VisionHandwritingModel GetVisionHandwritingModel(VisionHandwritingAttribute attribute) 53 | { 54 | 55 | if (attribute.ImageSource == Bindings.ImageSource.Client) 56 | { 57 | throw new ArgumentException($"ImageSource of Client does not support binding to vision models. Use Url or BlobStorage instead. "); 58 | } 59 | 60 | attribute.Validate(); 61 | 62 | var client = new VisionHandwritingClient(this, attribute, _loggerFactory); 63 | 64 | VisionHandwritingRequest request = new VisionHandwritingRequest(); 65 | 66 | if (attribute.ImageSource == ImageSource.BlobStorage) 67 | { 68 | var fileTask = StorageServices.GetFileBytes(attribute.BlobStoragePath, attribute.BlobStorageAccount); 69 | fileTask.Wait(); 70 | 71 | request.ImageBytes = fileTask.Result; 72 | 73 | } 74 | else 75 | { 76 | request.ImageUrl = attribute.ImageUrl; 77 | } 78 | 79 | var result = client.HandwritingAsync(request); 80 | result.Wait(); 81 | 82 | return result.Result; 83 | 84 | } 85 | 86 | 87 | 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/AzureFunctions.Extensions.CognitiveServices/Bindings/Vision/Handwriting/VisionHandwritingExtension.cs: -------------------------------------------------------------------------------- 1 | using AzureFunctions.Extensions.CognitiveServices.Bindings.Vision.Handwriting; 2 | using Microsoft.Azure.WebJobs; 3 | using System; 4 | 5 | namespace AzureFunctions.Extensions.CognitiveServices.Bindings.Vision.Handwriting 6 | { 7 | public static class VisionHandwritingExtension 8 | { 9 | public static IWebJobsBuilder AddVisionHandwriting(this IWebJobsBuilder builder) 10 | { 11 | if (builder == null) 12 | { 13 | throw new ArgumentNullException(nameof(builder)); 14 | } 15 | 16 | builder.AddExtension(); 17 | return builder; 18 | } 19 | 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /src/AzureFunctions.Extensions.CognitiveServices/Bindings/Vision/Handwriting/VisionHandwritingModel.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | 6 | namespace AzureFunctions.Extensions.CognitiveServices.Bindings.Vision.Handwriting 7 | { 8 | 9 | public class Word 10 | { 11 | 12 | [JsonProperty("boundingBox")] 13 | public IList BoundingBox { get; set; } 14 | 15 | [JsonProperty("text")] 16 | public string Text { get; set; } 17 | } 18 | 19 | public class Line 20 | { 21 | 22 | [JsonProperty("boundingBox")] 23 | public IList BoundingBox { get; set; } 24 | 25 | [JsonProperty("text")] 26 | public string Text { get; set; } 27 | 28 | [JsonProperty("words")] 29 | public IList Words { get; set; } 30 | } 31 | 32 | public class RecognitionResult 33 | { 34 | 35 | [JsonProperty("lines")] 36 | public IList Lines { get; set; } 37 | } 38 | 39 | public class VisionHandwritingModel 40 | { 41 | 42 | [JsonProperty("status")] 43 | public string Status { get; set; } 44 | 45 | [JsonProperty("succeeded")] 46 | public bool Succeeded { get; set; } 47 | 48 | [JsonProperty("failed")] 49 | public bool Failed { get; set; } 50 | 51 | [JsonProperty("finished")] 52 | public bool Finished { get; set; } 53 | 54 | [JsonProperty("recognitionResult")] 55 | public RecognitionResult RecognitionResult { get; set; } 56 | 57 | public override string ToString() 58 | { 59 | return JsonConvert.SerializeObject(this); 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/AzureFunctions.Extensions.CognitiveServices/Bindings/Vision/Handwriting/VisionHandwritingRequest.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.IO; 5 | using System.Text; 6 | 7 | namespace AzureFunctions.Extensions.CognitiveServices.Bindings.Vision.Handwriting 8 | { 9 | public class VisionHandwritingRequest : VisionRequestBase 10 | { 11 | 12 | public VisionHandwritingRequest() { } 13 | 14 | public VisionHandwritingRequest(Stream image) : base(image) { } 15 | 16 | public VisionHandwritingRequest(byte[] image) : base(image) { } 17 | 18 | public VisionHandwritingRequest(string imageUrl) : base(imageUrl) { } 19 | 20 | [JsonProperty("handwriting")] 21 | public bool Handwriting { get; set; } = true; 22 | 23 | } 24 | 25 | 26 | 27 | } 28 | -------------------------------------------------------------------------------- /src/AzureFunctions.Extensions.CognitiveServices/Bindings/Vision/Handwriting/VisionHandwritingStartup.cs: -------------------------------------------------------------------------------- 1 | using AzureFunctions.Extensions.CognitiveServices.Bindings.Vision.Domain; 2 | using AzureFunctions.Extensions.CognitiveServices.Bindings.Vision.Handwriting; 3 | using Microsoft.Azure.WebJobs; 4 | using Microsoft.Azure.WebJobs.Hosting; 5 | 6 | [assembly: WebJobsStartup(typeof(VisionHandwritingWebJobsStartup))] 7 | 8 | namespace AzureFunctions.Extensions.CognitiveServices.Bindings.Vision.Handwriting 9 | { 10 | 11 | public class VisionHandwritingWebJobsStartup : IWebJobsStartup 12 | { 13 | public void Configure(IWebJobsBuilder builder) 14 | { 15 | builder.AddVisionHandwriting(); 16 | } 17 | } 18 | 19 | 20 | } -------------------------------------------------------------------------------- /src/AzureFunctions.Extensions.CognitiveServices/Bindings/Vision/IVisionBinding.cs: -------------------------------------------------------------------------------- 1 | using AzureFunctions.Extensions.CognitiveServices.Services; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | 6 | namespace AzureFunctions.Extensions.CognitiveServices.Bindings.Vision 7 | { 8 | public interface IVisionBinding 9 | { 10 | ICognitiveServicesClient Client { get; set; } 11 | 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/AzureFunctions.Extensions.CognitiveServices/Bindings/Vision/Ocr/VisionOCRAttribute.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Azure.WebJobs.Description; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | 6 | namespace AzureFunctions.Extensions.CognitiveServices.Bindings.Vision.Ocr 7 | { 8 | [Binding] 9 | [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.ReturnValue)] 10 | public class VisionOcrAttribute : VisionAttributeBase 11 | { 12 | 13 | public bool? DetectOrientation { get; set; } 14 | 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/AzureFunctions.Extensions.CognitiveServices/Bindings/Vision/Ocr/VisionOCRBinding.cs: -------------------------------------------------------------------------------- 1 | using AzureFunctions.Extensions.CognitiveServices.Config; 2 | using AzureFunctions.Extensions.CognitiveServices.Services; 3 | using Microsoft.Azure.WebJobs.Description; 4 | using Microsoft.Azure.WebJobs.Host.Config; 5 | using Microsoft.Extensions.Logging; 6 | using System; 7 | using System.Collections.Generic; 8 | using System.Text; 9 | 10 | namespace AzureFunctions.Extensions.CognitiveServices.Bindings.Vision.Ocr 11 | { 12 | 13 | [Extension("VisionOcr")] 14 | public class VisionOcrBinding : IExtensionConfigProvider, IVisionBinding 15 | { 16 | 17 | public ICognitiveServicesClient Client { get; set; } 18 | 19 | internal ILoggerFactory _loggerFactory; 20 | internal ILogger _log; 21 | 22 | 23 | public VisionOcrBinding(ILoggerFactory loggerFactory, ICognitiveServicesClient client) 24 | { 25 | _loggerFactory = loggerFactory; 26 | this.Client = client; 27 | } 28 | 29 | public void Initialize(ExtensionConfigContext context) 30 | { 31 | 32 | LoadClient(); 33 | 34 | var visionRule = context.AddBindingRule(); 35 | 36 | visionRule.When(nameof(VisionOcrAttribute.ImageSource), ImageSource.BlobStorage) 37 | .BindToInput(GetVisionOcrModel); 38 | 39 | visionRule.When(nameof(VisionOcrAttribute.ImageSource), ImageSource.Url) 40 | .BindToInput(GetVisionOcrModel); 41 | 42 | visionRule.When(nameof(VisionOcrAttribute.ImageSource), ImageSource.Client) 43 | .BindToInput(attr => new VisionOcrClient(this, attr, _loggerFactory)); 44 | 45 | } 46 | 47 | private void LoadClient() 48 | { 49 | if (Client == null) 50 | { 51 | Client = new CognitiveServicesClient(new RetryPolicy(), _loggerFactory); 52 | } 53 | } 54 | 55 | private VisionOcrModel GetVisionOcrModel(VisionOcrAttribute attribute) 56 | { 57 | 58 | if (attribute.ImageSource == Bindings.ImageSource.Client) 59 | { 60 | throw new ArgumentException($"ImageSource of Client does not support binding to vision models. Use Url or BlobStorage instead. "); 61 | } 62 | 63 | attribute.Validate(); 64 | 65 | var client = new VisionOcrClient(this, attribute, _loggerFactory); 66 | 67 | VisionOcrRequest request = new VisionOcrRequest(); 68 | 69 | if (attribute.ImageSource == ImageSource.BlobStorage) 70 | { 71 | var fileTask = StorageServices.GetFileBytes(attribute.BlobStoragePath, attribute.BlobStorageAccount); 72 | fileTask.Wait(); 73 | 74 | request.ImageBytes = fileTask.Result; 75 | 76 | } 77 | else 78 | { 79 | request.ImageUrl = attribute.ImageUrl; 80 | } 81 | 82 | var result = client.OCRAsync(request); 83 | result.Wait(); 84 | 85 | return result.Result; 86 | 87 | } 88 | 89 | 90 | 91 | } 92 | } -------------------------------------------------------------------------------- /src/AzureFunctions.Extensions.CognitiveServices/Bindings/Vision/Ocr/VisionOCRClient.cs: -------------------------------------------------------------------------------- 1 | using AzureFunctions.Extensions.CognitiveServices.Bindings.Vision.Thumbnail; 2 | using AzureFunctions.Extensions.CognitiveServices.Config; 3 | using AzureFunctions.Extensions.CognitiveServices.Services; 4 | using AzureFunctions.Extensions.CognitiveServices.Services.Models; 5 | using Microsoft.Extensions.Logging; 6 | using Newtonsoft.Json; 7 | using System; 8 | using System.Collections.Generic; 9 | using System.Diagnostics; 10 | using System.Net.Http; 11 | using System.Text; 12 | using System.Threading.Tasks; 13 | 14 | namespace AzureFunctions.Extensions.CognitiveServices.Bindings.Vision.Ocr 15 | { 16 | public class VisionOcrClient 17 | { 18 | IVisionBinding _config; 19 | VisionOcrAttribute _attr; 20 | ILogger _log; 21 | 22 | public VisionOcrClient(IVisionBinding config, VisionOcrAttribute attr, ILoggerFactory loggerFactory) 23 | { 24 | this._config = config; 25 | this._attr = attr; 26 | this._log = loggerFactory?.CreateLogger("Host.Bindings.VisionOcr"); 27 | } 28 | 29 | public async Task OCRAsync(VisionOcrRequest request) 30 | { 31 | Stopwatch imageResizeSW = null; 32 | 33 | var visionOperation = await MergeProperties(request, this._config, this._attr); 34 | 35 | if (request.IsUrlImageSource == false) 36 | { 37 | 38 | if (visionOperation.ImageBytes == null || visionOperation.ImageBytes.Length == 0) 39 | { 40 | _log.LogWarning(VisionExceptionMessages.FileMissing); 41 | throw new ArgumentException(VisionExceptionMessages.FileMissing); 42 | } 43 | 44 | 45 | if (ImageResizeService.IsImage(visionOperation.ImageBytes) == false) 46 | { 47 | _log.LogWarning(VisionExceptionMessages.InvalidFileType); 48 | throw new ArgumentException(VisionExceptionMessages.InvalidFileType); 49 | } 50 | 51 | if (visionOperation.Oversized == true && visionOperation.AutoResize == false) 52 | { 53 | var message = string.Format(VisionExceptionMessages.FileTooLarge, 54 | VisionConfiguration.MaximumFileSize, visionOperation.ImageBytes.Length); 55 | _log.LogWarning(message); 56 | throw new ArgumentException(message); 57 | } 58 | else if (visionOperation.Oversized == true && visionOperation.AutoResize == true) 59 | { 60 | _log.LogTrace("Resizing Image"); 61 | 62 | imageResizeSW = new Stopwatch(); 63 | 64 | imageResizeSW.Start(); 65 | 66 | visionOperation.ImageBytes = ImageResizeService.ResizeImage(visionOperation.ImageBytes); 67 | 68 | imageResizeSW.Stop(); 69 | 70 | _log.LogMetric("VisionOcrImageResizeDurationMillisecond", imageResizeSW.ElapsedMilliseconds); 71 | 72 | if (visionOperation.Oversized) 73 | { 74 | var message = string.Format(VisionExceptionMessages.FileTooLargeAfterResize, 75 | VisionConfiguration.MaximumFileSize, visionOperation.ImageBytes.Length); 76 | _log.LogWarning(message); 77 | throw new ArgumentException(message); 78 | } 79 | 80 | } 81 | } 82 | 83 | var result = await SubmitRequest(visionOperation); 84 | 85 | return result; 86 | } 87 | 88 | private async Task SubmitRequest(VisionOcrRequest request) 89 | { 90 | Stopwatch sw = new Stopwatch(); 91 | 92 | //ocr/language=unk&detectOrientation=true 93 | string uri = $"{request.Url}/ocr?detectOrientation={request.DetectOrientation.ToString()}"; 94 | 95 | ServiceResultModel requestResult = null; 96 | 97 | if (request.IsUrlImageSource) 98 | { 99 | _log.LogTrace($"Submitting Vision Ocr Request"); 100 | 101 | var urlRequest = new VisionUrlRequest { Url = request.ImageUrl }; 102 | var requestContent = JsonConvert.SerializeObject(urlRequest); 103 | 104 | var content = new StringContent(requestContent); 105 | 106 | sw.Start(); 107 | 108 | requestResult = await this._config.Client.PostAsync(uri, request.Key, content, ReturnType.String); 109 | 110 | sw.Stop(); 111 | 112 | _log.LogMetric("VisionOCRDurationMillisecond", sw.ElapsedMilliseconds); 113 | 114 | } 115 | else 116 | { 117 | using (ByteArrayContent content = new ByteArrayContent(request.ImageBytes)) 118 | { 119 | requestResult = await this._config.Client.PostAsync(uri, request.Key, content, ReturnType.String); 120 | } 121 | } 122 | 123 | if (requestResult.HttpStatusCode == (int)System.Net.HttpStatusCode.OK) 124 | { 125 | _log.LogTrace($"OCR Request Results: {requestResult.Contents}"); 126 | 127 | VisionOcrModel result = JsonConvert.DeserializeObject(requestResult.Contents); 128 | 129 | return result; 130 | } 131 | else if (requestResult.HttpStatusCode == (int)System.Net.HttpStatusCode.BadRequest) 132 | { 133 | 134 | VisionErrorModel error = JsonConvert.DeserializeObject(requestResult.Contents); 135 | var message = string.Format(VisionExceptionMessages.CognitiveServicesException, error.Code, error.Message); 136 | 137 | _log.LogWarning(message); 138 | 139 | throw new Exception(message); 140 | } 141 | else 142 | { 143 | var message = string.Format(VisionExceptionMessages.CognitiveServicesException, requestResult.HttpStatusCode, requestResult.Contents); 144 | 145 | _log.LogError(message); 146 | 147 | throw new Exception(message); 148 | } 149 | 150 | } 151 | 152 | private async Task MergeProperties(VisionOcrRequest operation, IVisionBinding config, VisionOcrAttribute attr) 153 | { 154 | 155 | var visionOperation = new VisionOcrRequest 156 | { 157 | Url = attr.VisionUrl ?? operation.Url, 158 | Key = attr.VisionKey ?? operation.Key, 159 | AutoResize = attr.AutoResize, 160 | ImageUrl = string.IsNullOrEmpty(operation.ImageUrl) ? attr.ImageUrl : operation.ImageUrl, 161 | ImageBytes = operation.ImageBytes, 162 | DetectOrientation = attr.DetectOrientation.HasValue ? attr.DetectOrientation.Value : operation.DetectOrientation 163 | }; 164 | 165 | 166 | if (string.IsNullOrEmpty(visionOperation.Key)) 167 | { 168 | _log.LogWarning(VisionExceptionMessages.KeyMissing); 169 | throw new ArgumentException(VisionExceptionMessages.KeyMissing); 170 | } 171 | 172 | return visionOperation; 173 | 174 | } 175 | } 176 | } 177 | -------------------------------------------------------------------------------- /src/AzureFunctions.Extensions.CognitiveServices/Bindings/Vision/Ocr/VisionOCRExtension.cs: -------------------------------------------------------------------------------- 1 |  2 | using Microsoft.Azure.WebJobs; 3 | using System; 4 | 5 | namespace AzureFunctions.Extensions.CognitiveServices.Bindings.Vision.Ocr 6 | { 7 | public static class VisionOcrExtension 8 | { 9 | public static IWebJobsBuilder AddVisionOcr(this IWebJobsBuilder builder) 10 | { 11 | if (builder == null) 12 | { 13 | throw new ArgumentNullException(nameof(builder)); 14 | } 15 | 16 | builder.AddExtension(); 17 | return builder; 18 | } 19 | 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /src/AzureFunctions.Extensions.CognitiveServices/Bindings/Vision/Ocr/VisionOCRRequest.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.IO; 5 | using System.Text; 6 | 7 | namespace AzureFunctions.Extensions.CognitiveServices.Bindings.Vision.Ocr 8 | { 9 | public class VisionOcrRequest : VisionRequestBase 10 | { 11 | 12 | public VisionOcrRequest() { } 13 | 14 | public VisionOcrRequest(Stream image) : base(image) { } 15 | 16 | public VisionOcrRequest(byte[] image) : base(image) { } 17 | 18 | public VisionOcrRequest(string imageUrl) : base(imageUrl) { } 19 | 20 | [JsonProperty("detectOrientation")] 21 | public bool DetectOrientation { get; set; } = false; 22 | 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/AzureFunctions.Extensions.CognitiveServices/Bindings/Vision/Ocr/VisionOCRStartup.cs: -------------------------------------------------------------------------------- 1 | using AzureFunctions.Extensions.CognitiveServices.Bindings.Vision.Ocr; 2 | using Microsoft.Azure.WebJobs; 3 | using Microsoft.Azure.WebJobs.Hosting; 4 | 5 | [assembly: WebJobsStartup(typeof(VisionOcrWebJobsStartup))] 6 | 7 | namespace AzureFunctions.Extensions.CognitiveServices.Bindings.Vision.Ocr 8 | { 9 | 10 | public class VisionOcrWebJobsStartup : IWebJobsStartup 11 | { 12 | public void Configure(IWebJobsBuilder builder) 13 | { 14 | builder.AddVisionOcr(); 15 | } 16 | } 17 | 18 | 19 | } -------------------------------------------------------------------------------- /src/AzureFunctions.Extensions.CognitiveServices/Bindings/Vision/Ocr/VisionOcrModel.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | 6 | namespace AzureFunctions.Extensions.CognitiveServices.Bindings.Vision.Ocr 7 | { 8 | 9 | public class Word 10 | { 11 | 12 | [JsonProperty("boundingBox")] 13 | public string BoundingBox { get; set; } 14 | 15 | [JsonProperty("text")] 16 | public string Text { get; set; } 17 | } 18 | 19 | public class Line 20 | { 21 | 22 | [JsonProperty("boundingBox")] 23 | public string BoundingBox { get; set; } 24 | 25 | [JsonProperty("words")] 26 | public IList Words { get; set; } 27 | } 28 | 29 | public class Region 30 | { 31 | 32 | [JsonProperty("boundingBox")] 33 | public string BoundingBox { get; set; } 34 | 35 | [JsonProperty("lines")] 36 | public IList Lines { get; set; } 37 | } 38 | 39 | public class VisionOcrModel 40 | { 41 | 42 | [JsonProperty("textAngle")] 43 | public double TextAngle { get; set; } 44 | 45 | [JsonProperty("orientation")] 46 | public string Orientation { get; set; } 47 | 48 | [JsonProperty("language")] 49 | public string Language { get; set; } 50 | 51 | [JsonProperty("regions")] 52 | public IList Regions { get; set; } 53 | 54 | public override string ToString() 55 | { 56 | return JsonConvert.SerializeObject(this); 57 | } 58 | } 59 | 60 | } 61 | -------------------------------------------------------------------------------- /src/AzureFunctions.Extensions.CognitiveServices/Bindings/Vision/Thumbnail/VisionThumbnailAttribute.cs: -------------------------------------------------------------------------------- 1 | using AzureFunctions.Extensions.CognitiveServices.Config; 2 | using Microsoft.Azure.WebJobs.Description; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.ComponentModel.DataAnnotations; 6 | using System.Text; 7 | 8 | namespace AzureFunctions.Extensions.CognitiveServices.Bindings.Vision.Thumbnail 9 | { 10 | [Binding] 11 | [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.ReturnValue)] 12 | public class VisionThumbnailAttribute : VisionAttributeBase 13 | { 14 | [AutoResolve()] 15 | public string Width { get; set; } 16 | 17 | [AutoResolve()] 18 | public string Height { get; set; } 19 | 20 | public bool SmartCropping { get; set; } 21 | } 22 | } 23 | 24 | -------------------------------------------------------------------------------- /src/AzureFunctions.Extensions.CognitiveServices/Bindings/Vision/Thumbnail/VisionThumbnailBinding.cs: -------------------------------------------------------------------------------- 1 | using AzureFunctions.Extensions.CognitiveServices.Config; 2 | using AzureFunctions.Extensions.CognitiveServices.Services; 3 | using Microsoft.Azure.WebJobs.Description; 4 | using Microsoft.Azure.WebJobs.Host.Config; 5 | using Microsoft.Extensions.Logging; 6 | using System; 7 | using System.Collections.Generic; 8 | using System.Text; 9 | 10 | namespace AzureFunctions.Extensions.CognitiveServices.Bindings.Vision.Thumbnail 11 | { 12 | 13 | [Extension("VisionThumbnail")] 14 | public class VisionThumbnailBinding : IExtensionConfigProvider, IVisionBinding 15 | { 16 | 17 | public ICognitiveServicesClient Client { get; set; } 18 | 19 | internal ILoggerFactory _loggerFactory; 20 | 21 | public VisionThumbnailBinding(ILoggerFactory loggerFactory, ICognitiveServicesClient client) 22 | { 23 | _loggerFactory = loggerFactory; 24 | this.Client = client; 25 | } 26 | 27 | public void Initialize(ExtensionConfigContext context) 28 | { 29 | 30 | LoadClient(); 31 | 32 | var visionRule = context.AddBindingRule(); 33 | 34 | visionRule.When(nameof(VisionThumbnailAttribute.ImageSource), ImageSource.BlobStorage) 35 | .BindToInput(GetVisionDescribeModel); 36 | 37 | visionRule.When(nameof(VisionThumbnailAttribute.ImageSource), ImageSource.Url) 38 | .BindToInput(GetVisionDescribeModel); 39 | 40 | visionRule.When(nameof(VisionThumbnailAttribute.ImageSource), ImageSource.Client) 41 | .BindToInput(attr => new VisionThumbnailClient(this, attr, _loggerFactory)); 42 | 43 | 44 | } 45 | 46 | private void LoadClient() 47 | { 48 | if (Client == null) 49 | { 50 | Client = new CognitiveServicesClient(new RetryPolicy(), _loggerFactory); 51 | } 52 | } 53 | 54 | private Byte[] GetVisionDescribeModel(VisionThumbnailAttribute attribute) 55 | { 56 | 57 | if (attribute.ImageSource == Bindings.ImageSource.Client) 58 | { 59 | throw new ArgumentException($"ImageSource of Client does not support binding to vision models. Use Url or BlobStorage instead. "); 60 | } 61 | 62 | attribute.Validate(); 63 | 64 | var client = new VisionThumbnailClient(this, attribute, _loggerFactory); 65 | 66 | VisionThumbnailRequest request = new VisionThumbnailRequest(); 67 | 68 | if (attribute.ImageSource == ImageSource.BlobStorage) 69 | { 70 | var fileTask = StorageServices.GetFileBytes(attribute.BlobStoragePath, attribute.BlobStorageAccount); 71 | fileTask.Wait(); 72 | 73 | request.ImageBytes = fileTask.Result; 74 | } 75 | else 76 | { 77 | request.ImageUrl = attribute.ImageUrl; 78 | } 79 | 80 | var result = client.ThumbnailAsync(request); 81 | result.Wait(); 82 | 83 | return result.Result; 84 | 85 | } 86 | } 87 | } 88 | 89 | -------------------------------------------------------------------------------- /src/AzureFunctions.Extensions.CognitiveServices/Bindings/Vision/Thumbnail/VisionThumbnailClient.cs: -------------------------------------------------------------------------------- 1 | using AzureFunctions.Extensions.CognitiveServices.Config; 2 | using AzureFunctions.Extensions.CognitiveServices.Services; 3 | using AzureFunctions.Extensions.CognitiveServices.Services.Models; 4 | using Microsoft.Extensions.Logging; 5 | using Newtonsoft.Json; 6 | using System; 7 | using System.Collections.Generic; 8 | using System.Diagnostics; 9 | using System.Net.Http; 10 | using System.Text; 11 | using System.Threading.Tasks; 12 | 13 | namespace AzureFunctions.Extensions.CognitiveServices.Bindings.Vision.Thumbnail 14 | { 15 | public class VisionThumbnailClient 16 | { 17 | IVisionBinding _config; 18 | VisionThumbnailAttribute _attr; 19 | ILogger _log; 20 | 21 | public VisionThumbnailClient(IVisionBinding config, VisionThumbnailAttribute attr, ILoggerFactory loggerFactory) 22 | { 23 | this._config = config; 24 | this._attr = attr; 25 | this._log = loggerFactory?.CreateLogger("Host.Bindings.VisionThumbnail"); 26 | } 27 | 28 | public async Task ThumbnailAsync(VisionThumbnailRequest request) 29 | { 30 | Stopwatch imageResizeSW = null; 31 | 32 | var visionOperation = await MergeProperties(request, this._config, this._attr); 33 | 34 | if (request.IsUrlImageSource == false) 35 | { 36 | 37 | if (visionOperation.ImageBytes == null || visionOperation.ImageBytes.Length == 0) 38 | { 39 | _log.LogWarning(VisionExceptionMessages.FileMissing); 40 | throw new ArgumentException(VisionExceptionMessages.FileMissing); 41 | } 42 | 43 | if (ImageResizeService.IsImage(visionOperation.ImageBytes) == false) 44 | { 45 | _log.LogWarning(VisionExceptionMessages.InvalidFileType); 46 | throw new ArgumentException(VisionExceptionMessages.InvalidFileType); 47 | } 48 | 49 | 50 | if (visionOperation.Oversized == true && visionOperation.AutoResize == false) 51 | { 52 | var message = string.Format(VisionExceptionMessages.FileTooLarge, 53 | VisionConfiguration.MaximumFileSize, visionOperation.ImageBytes.Length); 54 | _log.LogWarning(message); 55 | throw new ArgumentException(message); 56 | } 57 | else if (visionOperation.Oversized == true && visionOperation.AutoResize == true) 58 | { 59 | _log.LogTrace("Resizing Image"); 60 | 61 | imageResizeSW = new Stopwatch(); 62 | 63 | imageResizeSW.Start(); 64 | 65 | visionOperation.ImageBytes = ImageResizeService.ResizeImage(visionOperation.ImageBytes); 66 | 67 | imageResizeSW.Stop(); 68 | 69 | _log.LogMetric("VisionAnalysisImageResizeDurationMillisecond", imageResizeSW.ElapsedMilliseconds); 70 | 71 | if (visionOperation.Oversized) 72 | { 73 | var message = string.Format(VisionExceptionMessages.FileTooLargeAfterResize, 74 | VisionConfiguration.MaximumFileSize, visionOperation.ImageBytes.Length); 75 | _log.LogWarning(message); 76 | throw new ArgumentException(message); 77 | } 78 | 79 | } 80 | } 81 | 82 | var result = await SubmitRequest(visionOperation); 83 | 84 | return result; 85 | } 86 | 87 | private async Task SubmitRequest(VisionThumbnailRequest request) 88 | { 89 | Stopwatch sw = new Stopwatch(); 90 | 91 | string uri = $"{request.Url}/generateThumbnail?width={request.Width}&height={request.Height}&smartCropping={request.SmartCropping.ToString()}"; 92 | 93 | ServiceResultModel requestResult = null; 94 | 95 | if (request.IsUrlImageSource) 96 | { 97 | _log.LogTrace($"Submitting Vision Thumbnail Request"); 98 | 99 | var urlRequest = new VisionUrlRequest { Url = request.ImageUrl }; 100 | var requestContent = JsonConvert.SerializeObject(urlRequest); 101 | 102 | var content = new StringContent(requestContent); 103 | 104 | sw.Start(); 105 | 106 | requestResult = await this._config.Client.PostAsync(uri, request.Key, content, ReturnType.Binary); 107 | 108 | sw.Stop(); 109 | 110 | _log.LogMetric("VisionRequestDurationMillisecond", sw.ElapsedMilliseconds); 111 | 112 | } 113 | else 114 | { 115 | using (ByteArrayContent content = new ByteArrayContent(request.ImageBytes)) 116 | { 117 | requestResult = await this._config.Client.PostAsync(uri, request.Key, content, ReturnType.Binary); 118 | } 119 | } 120 | 121 | if (requestResult.HttpStatusCode == (int)System.Net.HttpStatusCode.OK) 122 | { 123 | _log.LogTrace($"Thumbnail Request Results"); 124 | 125 | byte[] fileResult = requestResult.Binary; 126 | 127 | return fileResult; 128 | } 129 | else if (requestResult.HttpStatusCode == (int)System.Net.HttpStatusCode.BadRequest) 130 | { 131 | 132 | VisionErrorModel error = JsonConvert.DeserializeObject(requestResult.Contents); 133 | var message = string.Format(VisionExceptionMessages.CognitiveServicesException, error.Code, error.Message); 134 | 135 | _log.LogWarning(message); 136 | 137 | throw new Exception(message); 138 | } 139 | else 140 | { 141 | var message = string.Format(VisionExceptionMessages.CognitiveServicesException, requestResult.HttpStatusCode, requestResult.Contents); 142 | 143 | _log.LogError(message); 144 | 145 | throw new Exception(message); 146 | } 147 | 148 | } 149 | 150 | private async Task MergeProperties(VisionThumbnailRequest operation, IVisionBinding config, VisionThumbnailAttribute attr) 151 | { 152 | 153 | var visionOperation = new VisionThumbnailRequest 154 | { 155 | Url = attr.VisionUrl ?? operation.Url, 156 | Key = attr.VisionKey ?? operation.Key, 157 | AutoResize = attr.AutoResize, 158 | Height = attr.Height, 159 | Width = attr.Width, 160 | SmartCropping = attr.SmartCropping, 161 | ImageUrl = string.IsNullOrEmpty(operation.ImageUrl) ? attr.ImageUrl : operation.ImageUrl, 162 | ImageBytes = operation.ImageBytes, 163 | }; 164 | 165 | 166 | if (string.IsNullOrEmpty(visionOperation.Key)) 167 | { 168 | _log.LogWarning(VisionExceptionMessages.KeyMissing); 169 | throw new ArgumentException(VisionExceptionMessages.KeyMissing); 170 | } 171 | 172 | return visionOperation; 173 | 174 | } 175 | 176 | } 177 | } 178 | -------------------------------------------------------------------------------- /src/AzureFunctions.Extensions.CognitiveServices/Bindings/Vision/Thumbnail/VisionThumbnailExtension.cs: -------------------------------------------------------------------------------- 1 | using AzureFunctions.Extensions.CognitiveServices.Bindings.Vision.Thumbnail; 2 | using Microsoft.Azure.WebJobs; 3 | using System; 4 | 5 | namespace AzureFunctions.Extensions.CognitiveServices.Bindings.Vision.Thumbnail 6 | { 7 | public static class VisionThumbnailExtension 8 | { 9 | public static IWebJobsBuilder AddVisionThumbnail(this IWebJobsBuilder builder) 10 | { 11 | if (builder == null) 12 | { 13 | throw new ArgumentNullException(nameof(builder)); 14 | } 15 | 16 | builder.AddExtension(); 17 | return builder; 18 | } 19 | 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /src/AzureFunctions.Extensions.CognitiveServices/Bindings/Vision/Thumbnail/VisionThumbnailRequest.cs: -------------------------------------------------------------------------------- 1 | using AzureFunctions.Extensions.CognitiveServices.Config; 2 | using Microsoft.Azure.WebJobs.Description; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.ComponentModel.DataAnnotations; 6 | using System.IO; 7 | using System.Text; 8 | 9 | namespace AzureFunctions.Extensions.CognitiveServices.Bindings.Vision.Thumbnail 10 | { 11 | public class VisionThumbnailRequest : VisionRequestBase 12 | { 13 | 14 | public VisionThumbnailRequest() { } 15 | 16 | public VisionThumbnailRequest(Stream image) : base(image) { } 17 | 18 | public VisionThumbnailRequest(byte[] image) : base(image) { } 19 | 20 | public VisionThumbnailRequest(string imageUrl) : base(imageUrl) { } 21 | 22 | 23 | [AutoResolve()] 24 | [Required(ErrorMessage = VisionExceptionMessages.WidthMissing)] 25 | [Range(1, 1024, ErrorMessage = VisionExceptionMessages.ImageSizeOutOfRange)] 26 | public string Width { get; set; } 27 | 28 | [AutoResolve()] 29 | [Required(ErrorMessage = VisionExceptionMessages.HeightMissing)] 30 | [Range(1, 1024, ErrorMessage = VisionExceptionMessages.ImageSizeOutOfRange)] 31 | public string Height { get; set; } 32 | 33 | public bool SmartCropping { get; set; } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/AzureFunctions.Extensions.CognitiveServices/Bindings/Vision/Thumbnail/VisionThumbnailStartup.cs: -------------------------------------------------------------------------------- 1 | using AzureFunctions.Extensions.CognitiveServices.Bindings.Vision.Ocr; 2 | using AzureFunctions.Extensions.CognitiveServices.Bindings.Vision.Thumbnail; 3 | using Microsoft.Azure.WebJobs; 4 | using Microsoft.Azure.WebJobs.Hosting; 5 | 6 | [assembly: WebJobsStartup(typeof(VisionThumbnailWebJobsStartup))] 7 | 8 | namespace AzureFunctions.Extensions.CognitiveServices.Bindings.Vision.Thumbnail 9 | { 10 | 11 | public class VisionThumbnailWebJobsStartup : IWebJobsStartup 12 | { 13 | public void Configure(IWebJobsBuilder builder) 14 | { 15 | builder.AddVisionThumbnail(); 16 | } 17 | } 18 | 19 | 20 | } 21 | 22 | -------------------------------------------------------------------------------- /src/AzureFunctions.Extensions.CognitiveServices/Bindings/Vision/VisionAttributeBase.cs: -------------------------------------------------------------------------------- 1 | using AzureFunctions.Extensions.CognitiveServices.Config; 2 | using AzureFunctions.Extensions.CognitiveServices.Services; 3 | using Microsoft.Azure.WebJobs.Description; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.ComponentModel.DataAnnotations; 7 | using System.Text; 8 | 9 | namespace AzureFunctions.Extensions.CognitiveServices.Bindings 10 | { 11 | 12 | /// 13 | /// Determines the source of the image being analyzed. Each Image Source 14 | /// has varied required properties. 15 | /// 16 | public enum ImageSource 17 | { 18 | /// 19 | /// Image source is a publically accessible url 20 | /// 21 | Url, 22 | /// 23 | /// Image source is from Blob Storage 24 | /// 25 | BlobStorage, 26 | /// 27 | /// Image source is specified within the client binding 28 | /// 29 | Client 30 | } 31 | 32 | public abstract class VisionAttributeBase : Attribute 33 | { 34 | 35 | /// 36 | /// Vision Service URL. Defaults to an appSettings of VisionUrl 37 | /// 38 | [AppSetting(Default = "VisionUrl")] 39 | public string VisionUrl { get; set; } 40 | 41 | /// 42 | /// Authentication Key for Vision Service. Defaults to an appsettings 43 | /// of VisionKey 44 | /// 45 | [AppSetting(Default = "VisionKey")] 46 | public string VisionKey { get; set; } 47 | 48 | 49 | /// 50 | /// The source of the image being analyzed. 51 | /// 52 | public ImageSource ImageSource { get; set; } = ImageSource.Client; 53 | 54 | /// 55 | /// Whether the image being analyzed will automatically 56 | /// downscale and resize to achieve the cognitive services 57 | /// file size limit. 58 | /// 59 | public bool AutoResize { get; set; } = true; 60 | 61 | /// 62 | /// Vision Service URL. Defaults to a appsetting of VisionStorage 63 | /// 64 | [AppSetting(Default = "StorageAccount")] 65 | public string BlobStorageAccount { get; set; } 66 | 67 | /// 68 | /// Path to the file being analyzed in BlobStorage. May be set 69 | /// at runtime with dynamic parameters 70 | /// 71 | [AutoResolve()] 72 | public string BlobStoragePath { get; set; } 73 | 74 | /// 75 | /// A Url to the image being analyzed. Must be accessible without authentication 76 | /// 77 | [AutoResolve()] 78 | public string ImageUrl { get; set; } 79 | 80 | 81 | internal void Validate() 82 | { 83 | 84 | if (string.IsNullOrEmpty(VisionUrl)) 85 | { 86 | throw new ArgumentException($"A value for VisionUrl must be provided for a VisionAttribute."); 87 | } 88 | 89 | if (string.IsNullOrEmpty(VisionKey)) 90 | { 91 | throw new ArgumentException($"A value for VisionKey must be provided for a VisionAttribute."); 92 | } 93 | 94 | switch (ImageSource) 95 | { 96 | case ImageSource.Client: 97 | break; 98 | 99 | case ImageSource.BlobStorage: 100 | 101 | if (string.IsNullOrEmpty(BlobStorageAccount)) 102 | { 103 | throw new ArgumentException($"A value for BlobStorageConnection must be provided for an image source of BlobStorage"); 104 | } 105 | 106 | if (string.IsNullOrEmpty(BlobStoragePath)) 107 | { 108 | throw new ArgumentException($"A value for BlobStoragePath must be provided for an image source of BlobStorage"); 109 | } 110 | 111 | break; 112 | 113 | case ImageSource.Url: 114 | 115 | if (string.IsNullOrEmpty(ImageUrl)) 116 | { 117 | throw new ArgumentException($"A value for ImageUrl must be provided for an image source of BlobStorage"); 118 | } 119 | 120 | break; 121 | } 122 | 123 | } 124 | 125 | } 126 | } 127 | 128 | -------------------------------------------------------------------------------- /src/AzureFunctions.Extensions.CognitiveServices/Bindings/Vision/VisionErrorModel.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | 6 | namespace AzureFunctions.Extensions.CognitiveServices.Bindings.Vision 7 | { 8 | public class VisionErrorModel 9 | { 10 | [JsonProperty(PropertyName = "code")] 11 | public string Code { get; set; } 12 | 13 | [JsonProperty(PropertyName = "requestId")] 14 | public string RequestId { get; set; } 15 | 16 | [JsonProperty(PropertyName = "message")] 17 | public string Message { get; set; } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/AzureFunctions.Extensions.CognitiveServices/Bindings/Vision/VisionRequestBase.cs: -------------------------------------------------------------------------------- 1 | using AzureFunctions.Extensions.CognitiveServices.Config; 2 | using Newtonsoft.Json; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.IO; 6 | using System.Text; 7 | 8 | namespace AzureFunctions.Extensions.CognitiveServices.Bindings.Vision 9 | { 10 | public abstract class VisionRequestBase 11 | { 12 | public VisionRequestBase() { } 13 | 14 | public VisionRequestBase(Stream image) 15 | { 16 | this.ImageStream = image; 17 | } 18 | 19 | public VisionRequestBase(byte[] image) 20 | { 21 | this.ImageBytes = image; 22 | } 23 | public VisionRequestBase(string imageUrl) 24 | { 25 | this.ImageUrl = ImageUrl; 26 | } 27 | 28 | public Byte[] ImageBytes { get; set; } 29 | 30 | public Stream ImageStream 31 | { 32 | set 33 | { 34 | using (BinaryReader reader = new BinaryReader(value)) 35 | { 36 | this.ImageBytes = reader.ReadBytes((int)value.Length); 37 | } 38 | } 39 | } 40 | 41 | [JsonProperty("autoResizePhoto")] 42 | public bool AutoResize { get; set; } 43 | 44 | [JsonProperty("key")] 45 | public string Key { get; set; } 46 | 47 | [JsonProperty("url")] 48 | public string Url { get; set; } 49 | 50 | [JsonProperty("language")] 51 | public string Language { get; set; } = "en"; 52 | 53 | [JsonProperty("imageUrl")] 54 | public string ImageUrl { get; set; } 55 | 56 | public bool Oversized 57 | { 58 | get 59 | { 60 | var maxFileSize = VisionConfiguration.MaximumFileSize * 1024f * 1024f; 61 | 62 | if (ImageBytes.Length > maxFileSize) 63 | { 64 | return true; 65 | } 66 | 67 | return false; 68 | } 69 | } 70 | 71 | public bool IsUrlImageSource 72 | { 73 | get 74 | { 75 | if (ImageBytes == null || ImageBytes.Length == 0) 76 | { 77 | 78 | bool validUrl = Uri.TryCreate(ImageUrl, UriKind.Absolute, out Uri uriResult) 79 | && (uriResult.Scheme == Uri.UriSchemeHttp || uriResult.Scheme == Uri.UriSchemeHttps); 80 | 81 | if (validUrl) 82 | { 83 | return true; 84 | } 85 | 86 | } 87 | 88 | return false; 89 | } 90 | } 91 | 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/AzureFunctions.Extensions.CognitiveServices/Bindings/Vision/VisionUrlRequest.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | 6 | namespace AzureFunctions.Extensions.CognitiveServices.Bindings.Vision 7 | { 8 | public class VisionUrlRequest 9 | { 10 | 11 | [JsonProperty(PropertyName = "url")] 12 | public string Url { get; set; } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/AzureFunctions.Extensions.CognitiveServices/Config/RetryPolicy.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace AzureFunctions.Extensions.CognitiveServices.Config 6 | { 7 | public class RetryPolicy 8 | { 9 | public int MaxRetryAttemptsAfterThrottle { get; set; } = 3; 10 | 11 | public int MaxRetryWaitTimeInSeconds { get; set; } = 90; 12 | } 13 | 14 | public class PollingPolicy 15 | { 16 | public int MaxRetryAttempts { get; set; } = 10; 17 | 18 | public int WaitBetweenRetry { get; set; } = 1; 19 | 20 | public int MaxRetryWaitTimeInSeconds { get; set; } = 120; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/AzureFunctions.Extensions.CognitiveServices/Config/VisionConfiguration.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace AzureFunctions.Extensions.CognitiveServices.Config 6 | { 7 | public static class VisionConfiguration 8 | { 9 | public static readonly int MaximumFileSize = 4; 10 | public static readonly int DefaultResizeQuality = 70; 11 | } 12 | 13 | public static class VisionExceptionMessages 14 | { 15 | public const string CognitiveServicesException = "The following exception was returned from Cognitive Services. Code: {0} Message: {1}"; 16 | public const string InvalidFileType = "The file type you provided is not valid. Only jpg, gif, bmp, and png are supported."; 17 | public const string FileMissing = "Image File Missing"; 18 | public const string FileTooLarge = "Files must be {0} mb or smaller for the cognitive service vision API. Your file size is {1} bytes."; 19 | public const string FileTooLargeAfterResize = "Files must be {0} mb or smaller for the cognitive service vision API. After an autoresize attempt your file is still too large at {1} bytes."; 20 | public const string ImageSizeOutOfRange = "Must be between 1 and 1024. Recommended minimum of 50."; 21 | public const string WidthMissing = "Width is required."; 22 | public const string HeightMissing = "Height is required."; 23 | public const string SubscriptionUrlRequired = "The url for Cognitive Services endpoint is missing. This can be found in your Azure Subscription."; 24 | public const string SubscriptionKeyMissing = "The key for the Cognitive Services subscription is missing.This can be found in your Azure Subscription."; 25 | public const string KeyMissing = "The cognitives services key is missing. You must set the Key or SecureKey property."; 26 | public const string InvalidDomainName = "The provided domain option {0} is invalid. Domain must be Celebrity or Landmark. Use Domain constants for better accuracy."; 27 | public const string KeyvaultException = "An exception occured while attempting to obtain a keyvault value: {0}"; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/AzureFunctions.Extensions.CognitiveServices/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.CompilerServices; 2 | [assembly: InternalsVisibleTo("AzureFunctions.Extensions.CognitiveServices.Tests")] -------------------------------------------------------------------------------- /src/AzureFunctions.Extensions.CognitiveServices/Services/CognitiveServicesClient.cs: -------------------------------------------------------------------------------- 1 | using AzureFunctions.Extensions.CognitiveServices.Config; 2 | using AzureFunctions.Extensions.CognitiveServices.Services.Models; 3 | using Microsoft.Extensions.Logging; 4 | using Polly; 5 | using Polly.Timeout; 6 | using Polly.Wrap; 7 | using System; 8 | using System.Collections.Generic; 9 | using System.Net; 10 | using System.Net.Http; 11 | using System.Net.Http.Headers; 12 | using System.Text; 13 | using System.Threading.Tasks; 14 | 15 | namespace AzureFunctions.Extensions.CognitiveServices.Services 16 | { 17 | public class CognitiveServicesClient : ICognitiveServicesClient 18 | { 19 | private static HttpClient _client = new HttpClient(); 20 | private PolicyWrap _retryPolicyWrapper; 21 | private ILogger _log; 22 | 23 | 24 | public HttpClient GetHttpClientInstance() 25 | { 26 | return _client; 27 | } 28 | 29 | public CognitiveServicesClient(RetryPolicy retryPolicy, ILoggerFactory loggerFactory) 30 | { 31 | this._log = loggerFactory?.CreateLogger("Host.Bindings.CognitiveServicesClient"); 32 | 33 | Random jitter = new Random(); 34 | 35 | var timeoutPolicy = Policy 36 | .TimeoutAsync(TimeSpan.FromSeconds(retryPolicy.MaxRetryWaitTimeInSeconds), TimeoutStrategy.Pessimistic); 37 | 38 | var throttleRetryPolicy = Policy 39 | .HandleResult(r => r.StatusCode == (HttpStatusCode)429) 40 | .WaitAndRetryAsync(retryPolicy.MaxRetryAttemptsAfterThrottle, 41 | retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)) + TimeSpan.FromMilliseconds(jitter.Next(0, 1000)), 42 | onRetry: (exception, retryCount, context) => 43 | { 44 | _log.LogWarning($"Cognitive Service - Retry {retryCount} of {context.PolicyKey}, due to 429 throttling."); 45 | } 46 | ); 47 | 48 | 49 | _retryPolicyWrapper = timeoutPolicy.WrapAsync(throttleRetryPolicy); 50 | 51 | } 52 | 53 | public async Task PostAsync(string uri, string key, StringContent content, ReturnType returnType) 54 | { 55 | var httpResponse = await _retryPolicyWrapper.ExecuteAsync(async () => { 56 | 57 | _client.DefaultRequestHeaders.Add("Ocp-Apim-Subscription-Key", key); 58 | 59 | content.Headers.ContentType = new MediaTypeHeaderValue("application/json"); 60 | var requestMessage = new HttpRequestMessage(HttpMethod.Post, uri); 61 | 62 | var response = await _client.PostAsync(uri, content); 63 | 64 | return response; 65 | 66 | }); 67 | 68 | var result = new ServiceResultModel { HttpStatusCode = (int)httpResponse.StatusCode }; 69 | 70 | if (returnType == ReturnType.String) 71 | { 72 | result.Contents = await httpResponse.Content.ReadAsStringAsync(); 73 | result.Headers = httpResponse.Headers; 74 | } 75 | 76 | if (returnType == ReturnType.Binary) 77 | { 78 | result.Binary = await httpResponse.Content.ReadAsByteArrayAsync(); 79 | result.Headers = httpResponse.Headers; 80 | } 81 | 82 | 83 | return result; 84 | 85 | 86 | } 87 | 88 | public async Task PostAsync(string uri, string key, ByteArrayContent content, ReturnType returnType) 89 | { 90 | var httpResponse = await _retryPolicyWrapper.ExecuteAsync(async () => { 91 | 92 | _client.DefaultRequestHeaders.Add("Ocp-Apim-Subscription-Key", key); 93 | 94 | content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream"); 95 | var requestMessage = new HttpRequestMessage(HttpMethod.Post, uri); 96 | 97 | var response = await _client.PostAsync(uri, content); 98 | 99 | return response; 100 | 101 | }); 102 | 103 | var result = new ServiceResultModel { HttpStatusCode = (int)httpResponse.StatusCode }; 104 | 105 | if (returnType == ReturnType.String) 106 | { 107 | result.Contents = await httpResponse.Content.ReadAsStringAsync(); 108 | result.Headers = httpResponse.Headers; 109 | } 110 | 111 | if (returnType == ReturnType.Binary) 112 | { 113 | result.Binary = await httpResponse.Content.ReadAsByteArrayAsync(); 114 | result.Headers = httpResponse.Headers; 115 | } 116 | 117 | 118 | return result; 119 | } 120 | 121 | public async Task GetAsync(string uri, string key, ReturnType returnType) 122 | { 123 | var httpResponse = await _retryPolicyWrapper.ExecuteAsync(async () => { 124 | 125 | _client.DefaultRequestHeaders.Add("Ocp-Apim-Subscription-Key", key); 126 | 127 | var response = await _client.GetAsync(uri); 128 | 129 | return response; 130 | 131 | }); 132 | 133 | var result = new ServiceResultModel { HttpStatusCode = (int)httpResponse.StatusCode }; 134 | 135 | if (returnType == ReturnType.String) 136 | { 137 | result.Headers = httpResponse.Headers; 138 | result.Contents = await httpResponse.Content.ReadAsStringAsync(); 139 | } 140 | 141 | if (returnType == ReturnType.Binary) 142 | { 143 | result.Headers = httpResponse.Headers; 144 | result.Binary = await httpResponse.Content.ReadAsByteArrayAsync(); 145 | } 146 | 147 | return result; 148 | } 149 | } 150 | } 151 | -------------------------------------------------------------------------------- /src/AzureFunctions.Extensions.CognitiveServices/Services/ICognitiveServicesClient.cs: -------------------------------------------------------------------------------- 1 | using AzureFunctions.Extensions.CognitiveServices.Services.Models; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Net.Http; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace AzureFunctions.Extensions.CognitiveServices.Services 9 | { 10 | public enum ReturnType 11 | { 12 | String, 13 | Binary 14 | } 15 | 16 | public interface ICognitiveServicesClient 17 | { 18 | 19 | HttpClient GetHttpClientInstance(); 20 | 21 | Task PostAsync(string uri, string key, StringContent content, ReturnType returnType); 22 | 23 | Task PostAsync(string uri, string key, ByteArrayContent content, ReturnType returnType); 24 | 25 | Task GetAsync(string uri, string key, ReturnType returnType); 26 | 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/AzureFunctions.Extensions.CognitiveServices/Services/ImageResizeService.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Text; 6 | using AzureFunctions.Extensions.CognitiveServices.Config; 7 | using SixLabors.ImageSharp; 8 | using SixLabors.ImageSharp.Formats.Jpeg; 9 | using SixLabors.ImageSharp.Formats.Png; 10 | using SixLabors.ImageSharp.Processing; 11 | using SixLabors.ImageSharp.Processing.Transforms; 12 | using SixLabors.Primitives; 13 | 14 | namespace AzureFunctions.Extensions.CognitiveServices.Services 15 | { 16 | 17 | public class ImageResizeService 18 | { 19 | private const string JPG_HEADER = "FFD8FF"; 20 | private const string BMP_HEADER = "424D"; 21 | private const string GIF_HEADER = "474946"; 22 | private const string PNG_HEADER = "89504E470D0A1A0A"; 23 | 24 | /// 25 | /// Use the known header approach to identify if a file is an image 26 | /// (more reliable then just looking at the extension) 27 | /// 28 | /// 29 | /// 30 | public static bool IsImage(Byte[] file) 31 | { 32 | byte[] fileHeaderBuffer = new byte[8]; 33 | 34 | string header = string.Format("{0}{1}{2}{3}{4}{5}{6}{7}", 35 | file[0].ToString("X2"), 36 | file[1].ToString("X2"), 37 | file[2].ToString("X2"), 38 | file[3].ToString("X2"), 39 | file[4].ToString("X2"), 40 | file[5].ToString("X2"), 41 | file[6].ToString("X2"), 42 | file[7].ToString("X2")); 43 | 44 | if (header.StartsWith(JPG_HEADER) || 45 | header.StartsWith(GIF_HEADER) || 46 | header.StartsWith(BMP_HEADER) || 47 | header.StartsWith(PNG_HEADER)) 48 | { 49 | return true; 50 | } 51 | 52 | return false; 53 | 54 | } 55 | 56 | /// 57 | /// Resizes an image with the goal of getting the file under the default file size limit for Cognitive Services 58 | /// 59 | /// 60 | /// 61 | public static byte[] ResizeImage(Byte[] file) 62 | { 63 | 64 | using (var img = Image.Load(file)) 65 | { 66 | var jpegEncoder = new JpegEncoder() { Quality = VisionConfiguration.DefaultResizeQuality }; 67 | 68 | using (var resizedImage = new MemoryStream()) 69 | { 70 | 71 | var options = new ResizeOptions 72 | { 73 | Size = new Size(img.Width / 2, img.Height / 2), 74 | Mode = ResizeMode.Max 75 | }; 76 | 77 | img.Mutate(x => x.Resize(options)); 78 | 79 | img.SaveAsJpeg(resizedImage, jpegEncoder); 80 | 81 | byte[] results = resizedImage.ToArray(); 82 | 83 | return results; 84 | } 85 | 86 | } 87 | 88 | } 89 | 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/AzureFunctions.Extensions.CognitiveServices/Services/Models/ServiceResultModel.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Net.Http.Headers; 5 | using System.Text; 6 | 7 | namespace AzureFunctions.Extensions.CognitiveServices.Services.Models 8 | { 9 | public class ServiceResultModel 10 | { 11 | 12 | public int HttpStatusCode { get; set; } 13 | 14 | public string Contents { get; set; } 15 | 16 | public byte[] Binary { get; set; } 17 | 18 | public HttpResponseHeaders Headers { get; set; } 19 | 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/AzureFunctions.Extensions.CognitiveServices/Services/StorageServices.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.WindowsAzure.Storage; 2 | using System; 3 | using System.IO; 4 | using System.Threading.Tasks; 5 | 6 | namespace AzureFunctions.Extensions.CognitiveServices.Services 7 | { 8 | public class StorageServices 9 | { 10 | 11 | public static async Task GetFileBytes(string blobPath, string blobConnection) 12 | { 13 | CloudStorageAccount storageAccount; 14 | 15 | if (CloudStorageAccount.TryParse(blobConnection, out storageAccount)) 16 | { 17 | var path = Path.GetDirectoryName(blobPath); 18 | var filename = Path.GetFileName(blobPath); 19 | 20 | var blobClient = storageAccount.CreateCloudBlobClient(); 21 | var container = blobClient.GetContainerReference(path); 22 | var blob = container.GetBlockBlobReference(filename); 23 | 24 | using (MemoryStream memStream = new MemoryStream()) 25 | { 26 | await blob.DownloadToStreamAsync(memStream); 27 | 28 | var fileBytes = memStream.ToArray(); 29 | 30 | return fileBytes; 31 | } 32 | 33 | } 34 | else 35 | { 36 | throw new ArgumentException($"The storage account connection you provided is not valid."); 37 | } 38 | 39 | } 40 | 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /tests/AzureFunctions.Extensions.CognitiveServices.Tests/AzureFunctions.Extensions.CognitiveServices.Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp2.0 5 | false 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | all 16 | runtime; build; native; contentfiles; analyzers 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | True 28 | True 29 | MockResults.resx 30 | 31 | 32 | 33 | 34 | 35 | ResXFileCodeGenerator 36 | MockResults.Designer.cs 37 | 38 | 39 | 40 | 41 | 42 | PreserveNewest 43 | Never 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /tests/AzureFunctions.Extensions.CognitiveServices.Tests/Common/ExplicitTypeLocator.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Azure.WebJobs; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | 7 | namespace AzureFunctions.Extensions.CognitiveServices.Tests.Common 8 | { 9 | public class ExplicitTypeLocator : ITypeLocator 10 | { 11 | private readonly IReadOnlyList types; 12 | 13 | public ExplicitTypeLocator(params Type[] types) 14 | { 15 | this.types = types.ToList().AsReadOnly(); 16 | } 17 | 18 | public IReadOnlyList GetTypes() 19 | { 20 | return types; 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /tests/AzureFunctions.Extensions.CognitiveServices.Tests/Common/FunctionHost.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Azure.WebJobs; 2 | using Microsoft.Azure.WebJobs.Host; 3 | using Microsoft.Extensions.Primitives; 4 | using Moq; 5 | using System; 6 | using System.Collections.Generic; 7 | using System.IO; 8 | using System.Text; 9 | using System.Threading; 10 | using System.Threading.Tasks; 11 | 12 | namespace AzureFunctions.Extensions.CognitiveServices.Tests.TestHelpers 13 | { 14 | public abstract class FunctionHost 15 | { 16 | protected TraceWriter log = new VerboseDiagnosticsTraceWriter(); 17 | 18 | //public HttpRequest HttpRequestSetup(Dictionary query, string body) 19 | //{ 20 | // var reqMock = new Mock(); 21 | 22 | // reqMock.Setup(req => req.Query).Returns(new QueryCollection(query)); 23 | // var stream = new MemoryStream(); 24 | // var writer = new StreamWriter(stream); 25 | // writer.Write(body); 26 | // writer.Flush(); 27 | // stream.Position = 0; 28 | // reqMock.Setup(req => req.Body).Returns(stream); 29 | // return reqMock.Object; 30 | //} 31 | 32 | } 33 | 34 | public class AsyncCollector : IAsyncCollector 35 | { 36 | public readonly List Items = new List(); 37 | 38 | public Task AddAsync(T item, CancellationToken cancellationToken = default(CancellationToken)) 39 | { 40 | 41 | Items.Add(item); 42 | 43 | return Task.FromResult(true); 44 | } 45 | 46 | public Task FlushAsync(CancellationToken cancellationToken = default(CancellationToken)) 47 | { 48 | return Task.FromResult(true); 49 | } 50 | } 51 | } 52 | 53 | -------------------------------------------------------------------------------- /tests/AzureFunctions.Extensions.CognitiveServices.Tests/Common/LogMessage.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Logging; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | 6 | namespace AzureFunctions.Extensions.CognitiveServices.Tests.Common 7 | { 8 | public class LogMessage 9 | { 10 | public LogLevel Level { get; set; } 11 | 12 | public EventId EventId { get; set; } 13 | 14 | public IEnumerable> State { get; set; } 15 | 16 | public Exception Exception { get; set; } 17 | 18 | public string FormattedMessage { get; set; } 19 | 20 | public string Category { get; set; } 21 | 22 | public DateTime Timestamp { get; set; } 23 | 24 | public override string ToString() => $"[{Timestamp.ToString("HH:mm:ss.fff")}] [{Category}] {FormattedMessage} {Exception}"; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /tests/AzureFunctions.Extensions.CognitiveServices.Tests/Common/TestCognitiveServicesClient.cs: -------------------------------------------------------------------------------- 1 | using AzureFunctions.Extensions.CognitiveServices.Services; 2 | using AzureFunctions.Extensions.CognitiveServices.Services.Models; 3 | using AzureFunctions.Extensions.CognitiveServices.Tests.Resources; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Net.Http; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | 10 | namespace AzureFunctions.Extensions.CognitiveServices.Tests 11 | { 12 | public class TestCognitiveServicesClient : ICognitiveServicesClient 13 | { 14 | public Task GetAsync(string uri, string key, ReturnType returnType) 15 | { 16 | throw new NotImplementedException(); 17 | } 18 | 19 | public HttpClient GetHttpClientInstance() 20 | { 21 | return new HttpClient(); 22 | } 23 | 24 | public Task PostAsync(string uri, string key, StringContent content, ReturnType returnType) 25 | { 26 | ServiceResultModel result = null; 27 | 28 | if (uri.Contains("analyze") == true) 29 | { 30 | result = new ServiceResultModel { HttpStatusCode = 200, Contents = MockResults.VisionAnalysisResults }; 31 | } 32 | 33 | if (uri.Contains("vision") == true) 34 | { 35 | result = new ServiceResultModel { HttpStatusCode = 200, Contents = MockResults.VisionAnalysisResults }; 36 | } 37 | 38 | if (uri.Contains("describe") == true) 39 | { 40 | result = new ServiceResultModel { HttpStatusCode = 200, Contents = MockResults.VisionDescribeResults }; 41 | } 42 | 43 | if (returnType == ReturnType.Binary) 44 | { 45 | result = new ServiceResultModel { HttpStatusCode = 200, Binary = MockResults.SamplePhoto }; 46 | 47 | } 48 | 49 | return Task.FromResult(result); 50 | } 51 | 52 | public Task PostAsync(string uri, string key, ByteArrayContent content, ReturnType returnType) 53 | { 54 | ServiceResultModel result = null; 55 | 56 | if (returnType == ReturnType.String) 57 | { 58 | if (uri.Contains("analyze") == true) 59 | { 60 | result = new ServiceResultModel { HttpStatusCode = 200, Contents = MockResults.VisionAnalysisResults }; 61 | } 62 | 63 | if (uri.Contains("vision") == true) 64 | { 65 | result = new ServiceResultModel { HttpStatusCode = 200, Contents = MockResults.VisionAnalysisResults }; 66 | } 67 | 68 | if (uri.Contains("describe") == true) 69 | { 70 | result = new ServiceResultModel { HttpStatusCode = 200, Contents = MockResults.VisionDescribeResults }; 71 | } 72 | 73 | } 74 | 75 | if (returnType == ReturnType.Binary) 76 | { 77 | result = new ServiceResultModel { HttpStatusCode = 200, Binary = MockResults.SamplePhoto }; 78 | 79 | } 80 | 81 | return Task.FromResult(result); 82 | } 83 | 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /tests/AzureFunctions.Extensions.CognitiveServices.Tests/Common/TestHelper.cs: -------------------------------------------------------------------------------- 1 | using AzureFunctions.Extensions.CognitiveServices.Services; 2 | using Microsoft.Azure.WebJobs; 3 | using Microsoft.Azure.WebJobs.Host; 4 | using Microsoft.Azure.WebJobs.Host.Config; 5 | using Microsoft.Extensions.Configuration; 6 | using Microsoft.Extensions.DependencyInjection; 7 | using Microsoft.Extensions.Hosting; 8 | using Moq; 9 | using System; 10 | using System.Collections.Generic; 11 | using System.Diagnostics; 12 | using System.Text; 13 | using System.Threading.Tasks; 14 | 15 | namespace AzureFunctions.Extensions.CognitiveServices.Tests.Common 16 | { 17 | public static class TestHelper 18 | { 19 | private static readonly IConfiguration _emptyConfig = new ConfigurationBuilder().Build(); 20 | 21 | public static Task Await(Func condition, int timeout = 60 * 1000, int pollingInterval = 2 * 1000, bool throwWhenDebugging = false) 22 | { 23 | return Await(() => Task.FromResult(condition()), timeout, pollingInterval, throwWhenDebugging); 24 | } 25 | 26 | public static async Task Await(Func> condition, int timeout = 60 * 1000, int pollingInterval = 2 * 1000, bool throwWhenDebugging = false) 27 | { 28 | DateTime start = DateTime.Now; 29 | while (!await condition()) 30 | { 31 | await Task.Delay(pollingInterval); 32 | 33 | bool shouldThrow = !Debugger.IsAttached || (Debugger.IsAttached && throwWhenDebugging); 34 | if (shouldThrow && (DateTime.Now - start).TotalMilliseconds > timeout) 35 | { 36 | throw new ApplicationException("Condition not reached within timeout."); 37 | } 38 | } 39 | } 40 | 41 | public static JobHost GetJobHost(this IHost host) 42 | { 43 | return host.Services.GetService() as JobHost; 44 | } 45 | 46 | public static ExtensionConfigContext CreateExtensionConfigContext(INameResolver resolver) 47 | { 48 | var mockWebHookProvider = new Mock(); 49 | var mockExtensionRegistry = new Mock(); 50 | 51 | // TODO: ConverterManager needs to be fixed but this will work for now. 52 | IHost host = new HostBuilder() 53 | .ConfigureWebJobs() 54 | .Build(); 55 | 56 | var converterManager = host.Services.GetRequiredService(); 57 | 58 | return new ExtensionConfigContext(_emptyConfig, resolver, converterManager, mockWebHookProvider.Object, mockExtensionRegistry.Object); 59 | } 60 | 61 | [Obsolete()] 62 | public static async Task ExecuteFunction(ICognitiveServicesClient client, 63 | string functionReference) 64 | { 65 | } 66 | 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /tests/AzureFunctions.Extensions.CognitiveServices.Tests/Common/TestLogger.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Logging; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | 7 | namespace AzureFunctions.Extensions.CognitiveServices.Tests.Common 8 | { 9 | public class TestLogger : ILogger 10 | { 11 | private readonly Action _logAction; 12 | private IList _logMessages = new List(); 13 | 14 | // protect against changes to logMessages while enumerating 15 | private object _syncLock = new object(); 16 | 17 | public TestLogger(string category, Action logAction = null) 18 | { 19 | Category = category; 20 | _logAction = logAction; 21 | } 22 | 23 | public string Category { get; private set; } 24 | 25 | public IDisposable BeginScope(TState state) 26 | { 27 | return null; 28 | } 29 | 30 | public bool IsEnabled(LogLevel logLevel) 31 | { 32 | return true; 33 | } 34 | 35 | public IList GetLogMessages() 36 | { 37 | lock (_syncLock) 38 | { 39 | return _logMessages.ToList(); 40 | } 41 | } 42 | 43 | public void ClearLogMessages() 44 | { 45 | lock (_syncLock) 46 | { 47 | _logMessages.Clear(); 48 | } 49 | } 50 | 51 | public virtual void Log(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func formatter) 52 | { 53 | if (!IsEnabled(logLevel)) 54 | { 55 | return; 56 | } 57 | 58 | var logMessage = new LogMessage 59 | { 60 | Level = logLevel, 61 | EventId = eventId, 62 | State = state as IEnumerable>, 63 | Exception = exception, 64 | FormattedMessage = formatter(state, exception), 65 | Category = Category, 66 | Timestamp = DateTime.UtcNow 67 | }; 68 | 69 | lock (_syncLock) 70 | { 71 | _logMessages.Add(logMessage); 72 | } 73 | 74 | _logAction?.Invoke(logMessage); 75 | } 76 | 77 | public override string ToString() 78 | { 79 | return Category; 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /tests/AzureFunctions.Extensions.CognitiveServices.Tests/Common/TestLoggerProvider.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Logging; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Text.RegularExpressions; 7 | 8 | namespace AzureFunctions.Extensions.CognitiveServices.Tests.Common 9 | { 10 | public class TestLoggerProvider : ILoggerProvider 11 | { 12 | private readonly LoggerFilterOptions _filter; 13 | private readonly Action _logAction; 14 | private readonly Regex userCategoryRegex = new Regex(@"^Function\.\w+\.User$"); 15 | private readonly Dictionary _loggerCache = new Dictionary(); 16 | 17 | public TestLoggerProvider(Action logAction = null) 18 | { 19 | _filter = new LoggerFilterOptions(); 20 | _logAction = logAction; 21 | } 22 | 23 | public IList CreatedLoggers => _loggerCache.Values.ToList(); 24 | 25 | public ILogger CreateLogger(string categoryName) 26 | { 27 | if (!_loggerCache.TryGetValue(categoryName, out TestLogger logger)) 28 | { 29 | logger = new TestLogger(categoryName, _logAction); 30 | _loggerCache.Add(categoryName, logger); 31 | } 32 | 33 | return logger; 34 | } 35 | 36 | public IEnumerable GetAllLogMessages() => CreatedLoggers.SelectMany(l => l.GetLogMessages()).OrderBy(p => p.Timestamp); 37 | 38 | public IEnumerable GetAllUserLogMessages() 39 | { 40 | return GetAllLogMessages().Where(p => userCategoryRegex.IsMatch(p.Category)); 41 | } 42 | 43 | public string GetLogString() => string.Join(Environment.NewLine, GetAllLogMessages()); 44 | 45 | public void ClearAllLogMessages() 46 | { 47 | foreach (TestLogger logger in CreatedLoggers) 48 | { 49 | logger.ClearLogMessages(); 50 | } 51 | } 52 | 53 | public void Dispose() 54 | { 55 | } 56 | } 57 | 58 | 59 | 60 | } 61 | -------------------------------------------------------------------------------- /tests/AzureFunctions.Extensions.CognitiveServices.Tests/Common/TestNameResolver.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Azure.WebJobs; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | 6 | namespace AzureFunctions.Extensions.CognitiveServices.Tests.Common 7 | { 8 | public class TestNameResolver : INameResolver 9 | { 10 | private readonly Dictionary _values = new Dictionary(); 11 | private bool _throwException; 12 | 13 | public TestNameResolver(bool throwNotImplementedException = false) 14 | { 15 | // DefaultNameResolver throws so this helps simulate that for testing 16 | _throwException = throwNotImplementedException; 17 | } 18 | 19 | public Dictionary Values 20 | { 21 | get 22 | { 23 | return _values; 24 | } 25 | } 26 | 27 | public string Resolve(string name) 28 | { 29 | if (_throwException) 30 | { 31 | throw new NotImplementedException("INameResolver must be supplied to resolve '%" + name + "%'."); 32 | } 33 | 34 | string value = null; 35 | Values.TryGetValue(name, out value); 36 | return value; 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /tests/AzureFunctions.Extensions.CognitiveServices.Tests/Resources/MockResults.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.42000 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace AzureFunctions.Extensions.CognitiveServices.Tests.Resources { 12 | using System; 13 | 14 | 15 | /// 16 | /// A strongly-typed resource class, for looking up localized strings, etc. 17 | /// 18 | // This class was auto-generated by the StronglyTypedResourceBuilder 19 | // class via a tool like ResGen or Visual Studio. 20 | // To add or remove a member, edit your .ResX file then rerun ResGen 21 | // with the /str option, or rebuild your VS project. 22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")] 23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 25 | internal class MockResults { 26 | 27 | private static global::System.Resources.ResourceManager resourceMan; 28 | 29 | private static global::System.Globalization.CultureInfo resourceCulture; 30 | 31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] 32 | internal MockResults() { 33 | } 34 | 35 | /// 36 | /// Returns the cached ResourceManager instance used by this class. 37 | /// 38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 39 | internal static global::System.Resources.ResourceManager ResourceManager { 40 | get { 41 | if (object.ReferenceEquals(resourceMan, null)) { 42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("AzureFunctions.Extensions.CognitiveServices.Tests.Resources.MockResults", typeof(MockResults).Assembly); 43 | resourceMan = temp; 44 | } 45 | return resourceMan; 46 | } 47 | } 48 | 49 | /// 50 | /// Overrides the current thread's CurrentUICulture property for all 51 | /// resource lookups using this strongly typed resource class. 52 | /// 53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 54 | internal static global::System.Globalization.CultureInfo Culture { 55 | get { 56 | return resourceCulture; 57 | } 58 | set { 59 | resourceCulture = value; 60 | } 61 | } 62 | 63 | /// 64 | /// Looks up a localized resource of type System.Byte[]. 65 | /// 66 | internal static byte[] SamplePhoto { 67 | get { 68 | object obj = ResourceManager.GetObject("SamplePhoto", resourceCulture); 69 | return ((byte[])(obj)); 70 | } 71 | } 72 | 73 | /// 74 | /// Looks up a localized resource of type System.Byte[]. 75 | /// 76 | internal static byte[] SamplePhotoTooBig { 77 | get { 78 | object obj = ResourceManager.GetObject("SamplePhotoTooBig", resourceCulture); 79 | return ((byte[])(obj)); 80 | } 81 | } 82 | 83 | /// 84 | /// Looks up a localized string similar to { 85 | /// "tags": [ 86 | /// { 87 | /// "name": "outdoor", 88 | /// "score": 0.976 89 | /// }, 90 | /// { 91 | /// "name": "bird", 92 | /// "score": 0.95 93 | /// } 94 | /// ], 95 | /// "description": { 96 | /// "tags": [ 97 | /// "outdoor", 98 | /// "bird" 99 | /// ], 100 | /// "captions": [ 101 | /// { 102 | /// "text": "partridge in a pear tree", 103 | /// "confidence": 0.96 104 | /// } 105 | /// ] 106 | /// } 107 | ///}. 108 | /// 109 | internal static string VisionAnalysisResults { 110 | get { 111 | return ResourceManager.GetString("VisionAnalysisResults", resourceCulture); 112 | } 113 | } 114 | 115 | /// 116 | /// Looks up a localized string similar to { 117 | /// "description": { 118 | /// "tags": [ 119 | /// "person", 120 | /// "man", 121 | /// "outdoor", 122 | /// "window", 123 | /// "glasses" 124 | /// ], 125 | /// "captions": [ 126 | /// { 127 | /// "text": "Satya Nadella sitting on a bench", 128 | /// "confidence": 0.48293603002174407 129 | /// }, 130 | /// { 131 | /// "text": "Satya Nadella is sitting on a bench", 132 | /// "confidence": 0.40037006815422832 133 | /// }, 134 | /// { 135 | /// "text": "Satya Nadella sitting in front of a building", 136 | /// "confidence": 0.38035155997373377 137 | /// } 138 | /// ] [rest of string was truncated]";. 139 | /// 140 | internal static string VisionDescribeResults { 141 | get { 142 | return ResourceManager.GetString("VisionDescribeResults", resourceCulture); 143 | } 144 | } 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /tests/AzureFunctions.Extensions.CognitiveServices.Tests/Resources/MockResults.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | 121 | 122 | samplephoto.jpeg;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 123 | 124 | 125 | samplephototoobig.jpg;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 126 | 127 | 128 | visionanalysisresults.json;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8 129 | 130 | 131 | visiondescriberesults.json;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8 132 | 133 | -------------------------------------------------------------------------------- /tests/AzureFunctions.Extensions.CognitiveServices.Tests/Resources/SamplePhoto.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joshdcar/azure-functions-extensions-cognitive-services/f6dea9eff6c8d5a9cd1bb99fe21aa63efefac895/tests/AzureFunctions.Extensions.CognitiveServices.Tests/Resources/SamplePhoto.jpeg -------------------------------------------------------------------------------- /tests/AzureFunctions.Extensions.CognitiveServices.Tests/Resources/SamplePhotoTooBig.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joshdcar/azure-functions-extensions-cognitive-services/f6dea9eff6c8d5a9cd1bb99fe21aa63efefac895/tests/AzureFunctions.Extensions.CognitiveServices.Tests/Resources/SamplePhotoTooBig.jpg -------------------------------------------------------------------------------- /tests/AzureFunctions.Extensions.CognitiveServices.Tests/Resources/VisionAnalysisResults.json: -------------------------------------------------------------------------------- 1 | { 2 | "tags": [ 3 | { 4 | "name": "outdoor", 5 | "score": 0.976 6 | }, 7 | { 8 | "name": "bird", 9 | "score": 0.95 10 | } 11 | ], 12 | "description": { 13 | "tags": [ 14 | "outdoor", 15 | "bird" 16 | ], 17 | "captions": [ 18 | { 19 | "text": "partridge in a pear tree", 20 | "confidence": 0.96 21 | } 22 | ] 23 | } 24 | } -------------------------------------------------------------------------------- /tests/AzureFunctions.Extensions.CognitiveServices.Tests/Resources/VisionDescribeResults.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": { 3 | "tags": [ 4 | "person", 5 | "man", 6 | "outdoor", 7 | "window", 8 | "glasses" 9 | ], 10 | "captions": [ 11 | { 12 | "text": "Satya Nadella sitting on a bench", 13 | "confidence": 0.48293603002174407 14 | }, 15 | { 16 | "text": "Satya Nadella is sitting on a bench", 17 | "confidence": 0.40037006815422832 18 | }, 19 | { 20 | "text": "Satya Nadella sitting in front of a building", 21 | "confidence": 0.38035155997373377 22 | } 23 | ] 24 | }, 25 | "requestId": "ed2de1c6-fb55-4686-b0da-4da6e05d283f", 26 | "metadata": { 27 | "width": 1500, 28 | "height": 1000, 29 | "format": "Jpeg" 30 | } 31 | } -------------------------------------------------------------------------------- /tests/AzureFunctions.Extensions.CognitiveServices.Tests/TestHelper.cs: -------------------------------------------------------------------------------- 1 | using AzureFunctions.Extensions.CognitiveServices.Bindings.Vision; 2 | using AzureFunctions.Extensions.CognitiveServices.Bindings.Vision.Analysis; 3 | using AzureFunctions.Extensions.CognitiveServices.Bindings.Vision.Describe; 4 | using AzureFunctions.Extensions.CognitiveServices.Bindings.Vision.Handwriting; 5 | using AzureFunctions.Extensions.CognitiveServices.Bindings.Vision.Ocr; 6 | using AzureFunctions.Extensions.CognitiveServices.Bindings.Vision.Thumbnail; 7 | using AzureFunctions.Extensions.CognitiveServices.Services; 8 | using Microsoft.Azure.WebJobs; 9 | using Microsoft.Azure.WebJobs.Host.Config; 10 | using Microsoft.Extensions.Configuration; 11 | using Microsoft.Extensions.Hosting; 12 | using System; 13 | using System.Collections.Generic; 14 | using System.Threading.Tasks; 15 | using Microsoft.Extensions.DependencyInjection; 16 | using Microsoft.Extensions.Logging; 17 | using AzureFunctions.Extensions.CognitiveServices.Tests.Common; 18 | 19 | namespace AzureFunctions.Extensions.CognitiveServices.Tests 20 | { 21 | public class TestHelper2 22 | { 23 | private static string functionOut = null; 24 | 25 | public static async Task ExecuteFunction(ICognitiveServicesClient client, 26 | Type hostType, 27 | string functionReference) 28 | { 29 | 30 | 31 | //IExtensionConfigProvider binding = null; 32 | 33 | //if (typeof(BindingType) == typeof(VisionAnalysisBinding)) 34 | //{ 35 | // binding = new VisionAnalysisBinding(); 36 | //} 37 | 38 | //if (typeof(BindingType) == typeof(VisionDescribeBinding)) 39 | //{ 40 | // binding = new VisionDescribeBinding(); 41 | //} 42 | 43 | //if (typeof(BindingType) == typeof(VisionHandwritingBinding)) 44 | //{ 45 | // binding = new VisionHandwritingBinding(); 46 | //} 47 | 48 | //if (typeof(BindingType) == typeof(VisionOcrBinding)) 49 | //{ 50 | // binding = new VisionOcrBinding(); 51 | //} 52 | 53 | //if (typeof(BindingType) == typeof(VisionThumbnailBinding)) 54 | //{ 55 | // binding = new VisionThumbnailBinding(); 56 | //} 57 | 58 | //(binding as IVisionBinding).Client = client; 59 | 60 | 61 | //var jobHost = NewHost(binding); 62 | 63 | //var args = new Dictionary(); 64 | //await jobHost.CallAsync(functionReference, args); 65 | 66 | //Dummy data to use later 67 | //var args = new Dictionary{ 68 | // //{ "fileName", testFileName } 69 | //}; 70 | 71 | //// make sure we can write the file to data lake store 72 | //using (var host = await StartHostAsync(hostType)) 73 | //{ 74 | // await host.GetJobHost().CallAsync(functionReference, args); 75 | // functionOut = null; 76 | //} 77 | 78 | 79 | 80 | } 81 | 82 | //public static JobHost NewHost(IExtensionConfigProvider ext) 83 | //{ 84 | // var builder = new HostBuilder().ConfigureWebJobs(b => 85 | // b.AddAzureStorageCoreServices()); 86 | 87 | // var host = new JobHost() 88 | 89 | 90 | // //JobHostConfiguration config = new JobHostConfiguration(); 91 | // //config.HostId = Guid.NewGuid().ToString("n"); 92 | // //config.StorageConnectionString = null; 93 | // //config.DashboardConnectionString = null; 94 | // //config.TypeLocator = new FakeTypeLocator(); 95 | // //config.AddExtension(ext); 96 | // //config.NameResolver = new NameResolver(); 97 | 98 | // //var host = new JobHost() 99 | 100 | // //return host; 101 | // } 102 | 103 | //public static JobHost CreateJobHost(ILoggerProvider loggerProvider,INameResolver nameResolver) 104 | //{ 105 | // IHost host = new HostBuilder() 106 | // .ConfigureLogging( 107 | // loggingBuilder => 108 | // { 109 | // loggingBuilder.AddProvider(loggerProvider); 110 | // }) 111 | // .ConfigureWebJobs( 112 | // webJobsBuilder => 113 | // { 114 | // webJobsBuilder.AddAzureStorage(); 115 | // }) 116 | // .ConfigureServices( 117 | // serviceCollection => 118 | // { 119 | // ITypeLocator typeLocator = GetTypeLocator(); 120 | // serviceCollection.AddSingleton(typeLocator); 121 | // serviceCollection.AddSingleton(nameResolver); 122 | // }) 123 | // .Build(); 124 | 125 | // return (JobHost)host.Services.GetService(); 126 | //} 127 | 128 | 129 | 130 | } 131 | 132 | public class NameResolver : INameResolver 133 | { 134 | IConfigurationRoot _config; 135 | 136 | public NameResolver() 137 | { 138 | _config = new ConfigurationBuilder() 139 | .AddJsonFile("local.settings.json") 140 | .Build(); 141 | 142 | } 143 | public string Resolve(string name) 144 | { 145 | name = $"Values:{name}"; 146 | 147 | var value = _config[name].ToString(); 148 | 149 | return value; 150 | } 151 | } 152 | 153 | public class FakeTypeLocator : ITypeLocator 154 | { 155 | public IReadOnlyList Types => new Type[] { typeof(T) }; 156 | public IReadOnlyList GetTypes() 157 | { 158 | return Types; 159 | } 160 | } 161 | 162 | } 163 | -------------------------------------------------------------------------------- /tests/AzureFunctions.Extensions.CognitiveServices.Tests/TestHelpers/VerboseDiagnosticsTraceWriter.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Azure.WebJobs.Host; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Diagnostics; 5 | using System.Text; 6 | 7 | namespace AzureFunctions.Extensions.CognitiveServices.Tests.TestHelpers 8 | { 9 | public class VerboseDiagnosticsTraceWriter : TraceWriter 10 | { 11 | 12 | public VerboseDiagnosticsTraceWriter() : base(TraceLevel.Verbose) 13 | { 14 | 15 | } 16 | public override void Trace(TraceEvent traceEvent) 17 | { 18 | Debug.WriteLine(traceEvent.Message); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /tests/AzureFunctions.Extensions.CognitiveServices.Tests/VisionAnalysisTests.cs: -------------------------------------------------------------------------------- 1 | 2 | using AzureFunctions.Extensions.CognitiveServices.Bindings.Vision.Analysis; 3 | using AzureFunctions.Extensions.CognitiveServices.Config; 4 | using AzureFunctions.Extensions.CognitiveServices.Services; 5 | using AzureFunctions.Extensions.CognitiveServices.Tests.Common; 6 | using AzureFunctions.Extensions.CognitiveServices.Tests.Resources; 7 | using FluentAssertions; 8 | using Microsoft.Azure.WebJobs; 9 | using Microsoft.Extensions.Configuration; 10 | using Microsoft.Extensions.DependencyInjection; 11 | using Microsoft.Extensions.Hosting; 12 | using Microsoft.Extensions.Logging; 13 | using Newtonsoft.Json; 14 | using System; 15 | using System.Collections.Generic; 16 | using System.Threading.Tasks; 17 | using Xunit; 18 | 19 | namespace AzureFunctions.Extensions.CognitiveServices.Tests 20 | { 21 | 22 | public class VisionAnalysisTests 23 | { 24 | 25 | private static VisionAnalysisModel visionAnalysisUrlResult; 26 | private static VisionAnalysisModel visionAnalysisImageBytesResult; 27 | private static VisionAnalysisModel visionAnalysisImageBytesResizeResult; 28 | 29 | private static readonly TestLoggerProvider _loggerProvider = new TestLoggerProvider(); 30 | 31 | 32 | private static async Task RunTestAsync(string testName, object argument = null) 33 | { 34 | Type testType = typeof(VisionFunctions); 35 | var locator = new ExplicitTypeLocator(testType); 36 | ILoggerFactory loggerFactory = new LoggerFactory(); 37 | loggerFactory.AddProvider(_loggerProvider); 38 | ICognitiveServicesClient testCognitiveServicesClient = new TestCognitiveServicesClient(); 39 | 40 | var arguments = new Dictionary(); 41 | var resolver = new TestNameResolver(); 42 | 43 | IHost host = new HostBuilder() 44 | .ConfigureWebJobs(builder => 45 | { 46 | builder.AddVisionAnalysis(); 47 | }) 48 | .ConfigureServices(services => 49 | { 50 | services.AddSingleton(testCognitiveServicesClient); 51 | services.AddSingleton(resolver); 52 | services.AddSingleton(locator); 53 | }) 54 | .ConfigureLogging(logging => 55 | { 56 | logging.ClearProviders(); 57 | logging.AddProvider(_loggerProvider); 58 | }) 59 | .ConfigureAppConfiguration(c => 60 | { 61 | c.Sources.Clear(); 62 | 63 | var collection = new Dictionary 64 | { 65 | { "VisionKey", "1234XYZ" }, 66 | { "VisionUrl", "http://url" } 67 | }; 68 | 69 | c.AddInMemoryCollection(collection); 70 | }) 71 | .Build(); 72 | 73 | var method = testType.GetMethod(testName); 74 | 75 | await host.GetJobHost().CallAsync(method, arguments); 76 | } 77 | 78 | 79 | [Fact] 80 | public static async Task TestVisionAnalysisWithUrl() 81 | { 82 | 83 | var mockResult = JsonConvert.DeserializeObject(MockResults.VisionAnalysisResults); 84 | 85 | await RunTestAsync("VisionAnalysisWithUrl", null); 86 | 87 | var expectedResult = JsonConvert.SerializeObject(mockResult); 88 | var actualResult = JsonConvert.SerializeObject(visionAnalysisUrlResult); 89 | 90 | Assert.Equal(expectedResult, actualResult); 91 | } 92 | 93 | [Fact] 94 | public static async Task TestVisionAnalysisWithImageBytes() 95 | { 96 | 97 | var mockResult = JsonConvert.DeserializeObject(MockResults.VisionAnalysisResults); 98 | 99 | await RunTestAsync("VisionAnalysisWithImageBytes", null); 100 | 101 | var expectedResult = JsonConvert.SerializeObject(mockResult); 102 | var actualResult = JsonConvert.SerializeObject(visionAnalysisImageBytesResult); 103 | 104 | Assert.Equal(expectedResult, actualResult); 105 | } 106 | 107 | [Fact] 108 | public static async Task TestVisionAnalysisWithImageWithResize() 109 | { 110 | 111 | var mockResult = JsonConvert.DeserializeObject(MockResults.VisionAnalysisResults); 112 | 113 | await RunTestAsync("VisionAnalysisWithTooBigImageBytesWithResize", null); 114 | 115 | var expectedResult = JsonConvert.SerializeObject(mockResult); 116 | var actualResult = JsonConvert.SerializeObject(visionAnalysisImageBytesResizeResult); 117 | 118 | Assert.Equal(expectedResult, actualResult); 119 | } 120 | 121 | [Fact] 122 | public static async Task TestVisionAnalysisImageBytesTooLarge() 123 | { 124 | 125 | string exceptionMessage = "or smaller for the cognitive service vision API"; 126 | 127 | var exception = await Record.ExceptionAsync(() => RunTestAsync("VisionAnalysisWithTooBigImageBytes", null)); 128 | 129 | exception.Should().NotBeNull(); 130 | exception.InnerException.Should().NotBeNull(); 131 | exception.InnerException.Should().BeOfType(); 132 | exception.InnerException.Message.Should().Contain(exceptionMessage); 133 | 134 | } 135 | 136 | [Fact] 137 | public static async Task TestVisionAnalysisMissingFile() 138 | { 139 | 140 | var exception = await Record.ExceptionAsync(() => RunTestAsync("VisionAnalysisMissingFile", null)); 141 | 142 | exception.Should().NotBeNull(); 143 | exception.InnerException.Should().NotBeNull(); 144 | exception.InnerException.Should().BeOfType(); 145 | exception.InnerException.Message.Should().Contain(VisionExceptionMessages.FileMissing); 146 | 147 | } 148 | 149 | private class VisionFunctions 150 | { 151 | 152 | public static async Task VisionAnalysisWithUrl( 153 | [VisionAnalysis()] VisionAnalysisClient client) 154 | { 155 | 156 | var request = new VisionAnalysisRequest(); 157 | request.ImageUrl = "http://www.blah"; 158 | 159 | var result = await client.AnalyzeAsync(request); 160 | 161 | visionAnalysisUrlResult = result; 162 | } 163 | 164 | public static async Task VisionAnalysisWithImageBytes( 165 | [VisionAnalysis()] 166 | VisionAnalysisClient client) 167 | { 168 | var request = new VisionAnalysisRequest(); 169 | request.ImageBytes = MockResults.SamplePhoto; 170 | 171 | var result = await client.AnalyzeAsync(request); 172 | 173 | visionAnalysisImageBytesResult = result; 174 | } 175 | 176 | public static async Task VisionAnalysisWithTooBigImageBytes( 177 | [VisionAnalysis(AutoResize = false)] 178 | VisionAnalysisClient client) 179 | { 180 | 181 | var request = new VisionAnalysisRequest(); 182 | 183 | request.ImageBytes = MockResults.SamplePhotoTooBig; 184 | 185 | var result = await client.AnalyzeAsync(request); 186 | 187 | } 188 | 189 | public static async Task VisionAnalysisWithTooBigImageBytesWithResize( 190 | [VisionAnalysis(AutoResize = true)] 191 | VisionAnalysisClient client) 192 | { 193 | 194 | var request = new VisionAnalysisRequest(); 195 | request.ImageBytes = MockResults.SamplePhotoTooBig; 196 | 197 | var result = await client.AnalyzeAsync(request); 198 | 199 | visionAnalysisImageBytesResizeResult = result; 200 | 201 | } 202 | 203 | public static async Task VisionAnalysisMissingFile( 204 | [VisionAnalysis()] 205 | VisionAnalysisClient client) 206 | { 207 | 208 | var request = new VisionAnalysisRequest(); 209 | 210 | var result = await client.AnalyzeAsync(request); 211 | 212 | } 213 | 214 | public static async Task VisionAnalysisKeyvault( 215 | [VisionAnalysis()] 216 | VisionAnalysisClient client) 217 | { 218 | 219 | var request = new VisionAnalysisRequest(); 220 | 221 | var result = await client.AnalyzeAsync(request); 222 | 223 | } 224 | } 225 | 226 | } 227 | 228 | 229 | 230 | } 231 | -------------------------------------------------------------------------------- /tests/AzureFunctions.Extensions.CognitiveServices.Tests/VisionDescribeTests.cs: -------------------------------------------------------------------------------- 1 | using AzureFunctions.Extensions.CognitiveServices.Bindings.Vision.Analysis; 2 | using AzureFunctions.Extensions.CognitiveServices.Bindings.Vision.Describe; 3 | using AzureFunctions.Extensions.CognitiveServices.Bindings.Vision.Thumbnail; 4 | using AzureFunctions.Extensions.CognitiveServices.Config; 5 | using AzureFunctions.Extensions.CognitiveServices.Services; 6 | using AzureFunctions.Extensions.CognitiveServices.Tests.Common; 7 | using AzureFunctions.Extensions.CognitiveServices.Tests.Resources; 8 | using FluentAssertions; 9 | using Microsoft.Azure.WebJobs; 10 | using Microsoft.Extensions.Configuration; 11 | using Microsoft.Extensions.DependencyInjection; 12 | using Microsoft.Extensions.Hosting; 13 | using Microsoft.Extensions.Logging; 14 | using Newtonsoft.Json; 15 | using System; 16 | using System.Collections.Generic; 17 | using System.Text; 18 | using System.Threading.Tasks; 19 | using Xunit; 20 | 21 | namespace AzureFunctions.Extensions.CognitiveServices.Tests 22 | { 23 | public class VisionDescribeTests 24 | { 25 | private static VisionDescribeModel visionDescribeUrlResult; 26 | private static VisionDescribeModel visionDescribeImageBytesResult; 27 | private static VisionDescribeModel visionDescribeImageBytesResizeResult; 28 | 29 | private static readonly TestLoggerProvider _loggerProvider = new TestLoggerProvider(); 30 | 31 | 32 | private static async Task RunTestAsync(string testName, object argument = null) 33 | { 34 | Type testType = typeof(VisionFunctions); 35 | var locator = new ExplicitTypeLocator(testType); 36 | ILoggerFactory loggerFactory = new LoggerFactory(); 37 | loggerFactory.AddProvider(_loggerProvider); 38 | ICognitiveServicesClient testCognitiveServicesClient = new TestCognitiveServicesClient(); 39 | 40 | var arguments = new Dictionary(); 41 | var resolver = new TestNameResolver(); 42 | 43 | IHost host = new HostBuilder() 44 | .ConfigureWebJobs(builder => 45 | { 46 | builder.AddVisionDescribe(); 47 | }) 48 | .ConfigureServices(services => 49 | { 50 | services.AddSingleton(testCognitiveServicesClient); 51 | services.AddSingleton(resolver); 52 | services.AddSingleton(locator); 53 | }) 54 | .ConfigureLogging(logging => 55 | { 56 | logging.ClearProviders(); 57 | logging.AddProvider(_loggerProvider); 58 | }) 59 | .ConfigureAppConfiguration(c => 60 | { 61 | c.Sources.Clear(); 62 | 63 | var collection = new Dictionary 64 | { 65 | { "VisionKey", "1234XYZ" }, 66 | { "VisionUrl", "http://url" } 67 | }; 68 | 69 | c.AddInMemoryCollection(collection); 70 | }) 71 | .Build(); 72 | 73 | var method = testType.GetMethod(testName); 74 | 75 | await host.GetJobHost().CallAsync(method, arguments); 76 | } 77 | 78 | 79 | [Fact] 80 | public static async Task TestVisionAnalysisWithUrl() 81 | { 82 | 83 | var mockResult = JsonConvert.DeserializeObject(MockResults.VisionDescribeResults); 84 | 85 | await RunTestAsync("VisionDescribeWithUrl", null); 86 | 87 | var expectedResult = JsonConvert.SerializeObject(mockResult); 88 | var actualResult = JsonConvert.SerializeObject(visionDescribeUrlResult); 89 | 90 | Assert.Equal(expectedResult, actualResult); 91 | } 92 | 93 | [Fact] 94 | public static async Task TestVisionDescribeWithImageBytes() 95 | { 96 | 97 | var mockResult = JsonConvert.DeserializeObject(MockResults.VisionDescribeResults); 98 | 99 | await RunTestAsync("VisionDescribeWithImageBytes", null); 100 | 101 | var expectedResult = JsonConvert.SerializeObject(mockResult); 102 | var actualResult = JsonConvert.SerializeObject(visionDescribeImageBytesResult); 103 | 104 | Assert.Equal(expectedResult, actualResult); 105 | } 106 | 107 | [Fact] 108 | public static async Task TestVisionDescribeWithImageWithResize() 109 | { 110 | var mockResult = JsonConvert.DeserializeObject(MockResults.VisionDescribeResults); 111 | 112 | await RunTestAsync("VisionDescribeWithTooBigImageBytesWithResize", null); 113 | 114 | var expectedResult = JsonConvert.SerializeObject(mockResult); 115 | var actualResult = JsonConvert.SerializeObject(visionDescribeImageBytesResizeResult); 116 | 117 | Assert.Equal(expectedResult, actualResult); 118 | } 119 | 120 | [Fact] 121 | public static async Task TestVisionDescribeImageBytesTooLarge() 122 | { 123 | 124 | string exceptionMessage = "or smaller for the cognitive service vision API"; 125 | 126 | var exception = await Record.ExceptionAsync(() => RunTestAsync("VisionDescribeWithTooBigImageBytes", null)); 127 | 128 | exception.Should().NotBeNull(); 129 | exception.InnerException.Should().NotBeNull(); 130 | exception.InnerException.Should().BeOfType(); 131 | exception.InnerException.Message.Should().Contain(exceptionMessage); 132 | 133 | } 134 | 135 | 136 | [Fact] 137 | public static async Task TestVisionDescribeMissingFile() 138 | { 139 | 140 | var exception = await Record.ExceptionAsync(() => RunTestAsync("VisionDescribeMissingFile", null)); 141 | 142 | exception.Should().NotBeNull(); 143 | exception.InnerException.Should().NotBeNull(); 144 | exception.InnerException.Should().BeOfType(); 145 | exception.InnerException.Message.Should().Contain(VisionExceptionMessages.FileMissing); 146 | 147 | } 148 | 149 | private class VisionFunctions 150 | { 151 | 152 | public async Task VisionDescribeWithUrl( 153 | [VisionDescribe()] 154 | VisionDescribeClient client) 155 | { 156 | var request = new VisionDescribeRequest(); 157 | request.ImageUrl = "http://www.blah"; 158 | 159 | var result = await client.DescribeAsync(request); 160 | 161 | visionDescribeUrlResult = result; 162 | } 163 | 164 | public async Task VisionDescribeWithImageBytes( 165 | [VisionDescribe()] 166 | VisionDescribeClient client) 167 | { 168 | var request = new VisionDescribeRequest(); 169 | request.ImageBytes = Resources.MockResults.SamplePhoto; 170 | 171 | var result = await client.DescribeAsync(request); 172 | 173 | visionDescribeImageBytesResult = result; 174 | } 175 | 176 | public async Task VisionDescribeWithTooBigImageBytes( 177 | [VisionDescribe(AutoResize=false)] 178 | VisionDescribeClient client) 179 | { 180 | 181 | var request = new VisionDescribeRequest(); 182 | request.AutoResize = false; 183 | request.ImageBytes = MockResults.SamplePhotoTooBig; 184 | 185 | var result = await client.DescribeAsync(request); 186 | 187 | } 188 | 189 | public async Task VisionDescribeWithTooBigImageBytesWithResize( 190 | [VisionDescribe()] 191 | VisionDescribeClient client) 192 | { 193 | 194 | var request = new VisionDescribeRequest(); 195 | request.ImageBytes = MockResults.SamplePhotoTooBig; 196 | 197 | var result = await client.DescribeAsync(request); 198 | 199 | visionDescribeImageBytesResizeResult = result; 200 | 201 | } 202 | 203 | public async Task VisionDescribeMissingFile( 204 | [VisionDescribe()] 205 | VisionDescribeClient client) 206 | { 207 | 208 | var request = new VisionDescribeRequest(); 209 | 210 | var result = await client.DescribeAsync(request); 211 | 212 | } 213 | 214 | public async Task VisionDescribeKeyvault( 215 | [VisionDescribe()] 216 | VisionDescribeClient client) 217 | { 218 | 219 | var request = new VisionDescribeRequest(); 220 | 221 | var result = await client.DescribeAsync(request); 222 | 223 | } 224 | } 225 | } 226 | } 227 | -------------------------------------------------------------------------------- /tests/AzureFunctions.Extensions.CognitiveServices.Tests/VisionDomainTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace AzureFunctions.Extensions.CognitiveServices.Tests 6 | { 7 | class VisionDomainTests 8 | { 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /tests/AzureFunctions.Extensions.CognitiveServices.Tests/VisionHandwritingTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace AzureFunctions.Extensions.CognitiveServices.Tests 6 | { 7 | class VisionHandwritingTests 8 | { 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /tests/AzureFunctions.Extensions.CognitiveServices.Tests/VisionOcrTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace AzureFunctions.Extensions.CognitiveServices.Tests 6 | { 7 | class VisionOcrTests 8 | { 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /tests/AzureFunctions.Extensions.CognitiveServices.Tests/VisionThumbnailTests.cs: -------------------------------------------------------------------------------- 1 | using AzureFunctions.Extensions.CognitiveServices.Bindings.Vision.Thumbnail; 2 | using AzureFunctions.Extensions.CognitiveServices.Config; 3 | using AzureFunctions.Extensions.CognitiveServices.Services; 4 | using AzureFunctions.Extensions.CognitiveServices.Tests.Common; 5 | using AzureFunctions.Extensions.CognitiveServices.Tests.Resources; 6 | using FluentAssertions; 7 | using Microsoft.Azure.WebJobs; 8 | using Microsoft.Extensions.Configuration; 9 | using Microsoft.Extensions.DependencyInjection; 10 | using Microsoft.Extensions.Hosting; 11 | using Microsoft.Extensions.Logging; 12 | using Newtonsoft.Json; 13 | using System; 14 | using System.Collections.Generic; 15 | using System.Text; 16 | using System.Threading.Tasks; 17 | using Xunit; 18 | 19 | namespace AzureFunctions.Extensions.CognitiveServices.Tests 20 | { 21 | public class VisionThumbnailTests 22 | { 23 | private static byte[] visionThumbnailResult; 24 | 25 | private static readonly TestLoggerProvider _loggerProvider = new TestLoggerProvider(); 26 | 27 | 28 | private static async Task RunTestAsync(string testName, object argument = null) 29 | { 30 | Type testType = typeof(VisionFunctions); 31 | var locator = new ExplicitTypeLocator(testType); 32 | ILoggerFactory loggerFactory = new LoggerFactory(); 33 | loggerFactory.AddProvider(_loggerProvider); 34 | ICognitiveServicesClient testCognitiveServicesClient = new TestCognitiveServicesClient(); 35 | 36 | var arguments = new Dictionary(); 37 | var resolver = new TestNameResolver(); 38 | 39 | IHost host = new HostBuilder() 40 | .ConfigureWebJobs(builder => 41 | { 42 | builder.AddVisionThumbnail(); 43 | }) 44 | .ConfigureServices(services => 45 | { 46 | services.AddSingleton(testCognitiveServicesClient); 47 | services.AddSingleton(resolver); 48 | services.AddSingleton(locator); 49 | }) 50 | .ConfigureLogging(logging => 51 | { 52 | logging.ClearProviders(); 53 | logging.AddProvider(_loggerProvider); 54 | }) 55 | .ConfigureAppConfiguration(c => 56 | { 57 | c.Sources.Clear(); 58 | 59 | var collection = new Dictionary 60 | { 61 | { "VisionKey", "1234XYZ" }, 62 | { "VisionUrl", "http://url" } 63 | }; 64 | 65 | c.AddInMemoryCollection(collection); 66 | }) 67 | .Build(); 68 | 69 | var method = testType.GetMethod(testName); 70 | 71 | await host.GetJobHost().CallAsync(method, arguments); 72 | } 73 | 74 | 75 | [Fact] 76 | public static async Task TestVisionThumbnailWithUrl() 77 | { 78 | await RunTestAsync("VisionThumbnailWithUrl", null); 79 | 80 | Assert.Equal(MockResults.SamplePhoto.Length, visionThumbnailResult.Length); 81 | } 82 | 83 | [Fact] 84 | public static async Task TestVisionThumbnailWithImageBytes() 85 | { 86 | await RunTestAsync("VisionThumbnailWithImageBytes", null); 87 | 88 | Assert.Equal(MockResults.SamplePhoto.Length, visionThumbnailResult.Length); 89 | } 90 | 91 | [Fact] 92 | public static async Task TestVisionThumbnailWithImageWithResize() 93 | { 94 | await RunTestAsync("VisionThumbnailWithTooBigImageBytesWithResize", null); 95 | 96 | Assert.Equal(MockResults.SamplePhoto.Length, visionThumbnailResult.Length); 97 | 98 | } 99 | 100 | [Fact] 101 | public static async Task TestVisionThumbnailImageBytesTooLarge() 102 | { 103 | 104 | string exceptionMessage = "or smaller for the cognitive service vision API"; 105 | 106 | var exception = await Record.ExceptionAsync(() => RunTestAsync("VisionThumbnailWithTooBigImageBytes", null)); 107 | 108 | exception.Should().NotBeNull(); 109 | exception.InnerException.Should().NotBeNull(); 110 | exception.InnerException.Should().BeOfType(); 111 | exception.InnerException.Message.Should().Contain(exceptionMessage); 112 | 113 | } 114 | 115 | [Fact] 116 | public static async Task TestVisionThumbnailMissingFile() 117 | { 118 | 119 | var exception = await Record.ExceptionAsync(() => RunTestAsync("VisionThumbnailMissingFile", null)); 120 | 121 | exception.Should().NotBeNull(); 122 | exception.InnerException.Should().NotBeNull(); 123 | exception.InnerException.Should().BeOfType(); 124 | exception.InnerException.Message.Should().Contain(VisionExceptionMessages.FileMissing); 125 | 126 | } 127 | 128 | private class VisionFunctions 129 | { 130 | 131 | public async Task VisionThumbnailWithUrl( 132 | [VisionThumbnail( Width ="100", Height="100")] 133 | VisionThumbnailClient client) 134 | { 135 | var request = new VisionThumbnailRequest(); 136 | request.ImageUrl = "http://www.blah"; 137 | 138 | var result = await client.ThumbnailAsync(request); 139 | 140 | visionThumbnailResult = result; 141 | } 142 | 143 | public async Task VisionThumbnailWithImageBytes( 144 | [VisionThumbnail(Width ="100", Height="100")] 145 | VisionThumbnailClient client) 146 | { 147 | var request = new VisionThumbnailRequest(); 148 | request.ImageBytes = MockResults.SamplePhoto; 149 | 150 | var result = await client.ThumbnailAsync(request); 151 | 152 | visionThumbnailResult = result; 153 | } 154 | 155 | public async Task VisionThumbnailWithTooBigImageBytes( 156 | [VisionThumbnail(AutoResize = false, Width ="100", Height="100")] 157 | VisionThumbnailClient client) 158 | { 159 | 160 | var request = new VisionThumbnailRequest(); 161 | request.ImageBytes = MockResults.SamplePhotoTooBig; 162 | 163 | var result = await client.ThumbnailAsync(request); 164 | 165 | } 166 | 167 | public async Task VisionThumbnailWithTooBigImageBytesWithResize( 168 | [VisionThumbnail(AutoResize = true, Width ="100", Height="100")] 169 | VisionThumbnailClient client) 170 | { 171 | 172 | var request = new VisionThumbnailRequest(); 173 | request.ImageBytes = MockResults.SamplePhotoTooBig; 174 | 175 | var result = await client.ThumbnailAsync(request); 176 | 177 | visionThumbnailResult = result; 178 | 179 | } 180 | 181 | public async Task VisionThumbnailMissingFile( 182 | [VisionThumbnail(Width ="100", Height="100")] 183 | VisionThumbnailClient client) 184 | { 185 | 186 | var request = new VisionThumbnailRequest(); 187 | 188 | var result = await client.ThumbnailAsync(request); 189 | 190 | } 191 | 192 | public async Task VisionThumbnailKeyvault( 193 | [VisionThumbnail(Width ="100", Height="100")] 194 | VisionThumbnailClient client) 195 | { 196 | 197 | var request = new VisionThumbnailRequest(); 198 | 199 | var result = await client.ThumbnailAsync(request); 200 | 201 | } 202 | } 203 | } 204 | } 205 | -------------------------------------------------------------------------------- /tests/AzureFunctions.Extensions.CognitiveServices.Tests/local.settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "IsEncrypted": false, 3 | "Values": { 4 | "AzureWebJobsStorage": "UseDevelopmentStorage=true", 5 | "AzureWebJobsDashboard": "UseDevelopmentStorage=true", 6 | "FUNCTIONS_WORKER_RUNTIME": "dotnet", 7 | "VisionKey": "XXXXXXXXXXXXXXXXXXXXXX", 8 | "VisionUrl": "XXXXXXXXXXXXXXXXXXXXXX" 9 | } 10 | } --------------------------------------------------------------------------------