├── doc
├── res
│ ├── cl-api-key.jpg
│ ├── ma-signin.jpg
│ ├── gcp-activate.jpg
│ ├── firstrun-mac-exe.jpg
│ ├── gcp-project-new.jpg
│ ├── gcp-signup-step1.jpg
│ ├── gui-cl-api-key.jpg
│ ├── gui-os-endpoint.jpg
│ ├── gui-web-urls-txt.jpg
│ ├── gui-win-a-folder.jpg
│ ├── gui-win-progress.jpg
│ ├── gui-win-settings.jpg
│ ├── gui-win-txt-file.jpg
│ ├── gcp-project-naming.jpg
│ ├── gcp-signup-step2.1.jpg
│ ├── gcp-signup-step2.2.jpg
│ ├── ma-add-a-resource.jpg
│ ├── ma-cogserv-naming.jpg
│ ├── ma-confirm-create.jpg
│ ├── ma-go-to-resource.jpg
│ ├── ma-resource-search.jpg
│ ├── cl-application-naming.jpg
│ ├── cl-create-application.jpg
│ ├── firstrun-windows-exe.jpg
│ ├── gcp-credential-create.jpg
│ ├── gcp-credential-done.jpg
│ ├── gcp-credential-naming.jpg
│ ├── gcp-credential-role1.jpg
│ ├── gcp-credential-role2.jpg
│ ├── gcp-credential-role3.jpg
│ ├── gcp-enableapi-enable.jpg
│ ├── gcp-enableapi-search.jpg
│ ├── gcp-enableapi-select.jpg
│ ├── gui-cl-api-key-field.jpg
│ ├── gui-ma-key-endpoint.jpg
│ ├── gui-win-invokebutton.jpg
│ ├── gui-win-outputfiles.jpg
│ ├── gui-win-web-locations.jpg
│ ├── ma-cognitive-create.jpg
│ ├── ma-keys-and-endpoint.jpg
│ ├── ma-keys-endpoint-copy.jpg
│ ├── ma-region-selection.jpg
│ ├── ma-res-group-naming.jpg
│ ├── cl-application-created.jpg
│ ├── firstrun-mac-cannotrun.jpg
│ ├── firstrun-mac-openanyway.jpg
│ ├── gcp-credential-navigate.jpg
│ ├── gcp-enableapi-navigate.jpg
│ ├── gui-cl-model-selection.jpg
│ ├── gui-gcp-credential-file.jpg
│ ├── gui-os-model-selection.jpg
│ ├── firstrun-mac-openrelease.jpg
│ ├── firstrun-windows-moreinfo.jpg
│ ├── firstrun-windows-runanyway.jpg
│ ├── gcp-credential-createjson.jpg
│ ├── gcp-credential-createkey.jpg
│ ├── gui-az-endpoint-key-fields.jpg
│ ├── gui-az-feature-selection.jpg
│ ├── gui-gv-feature-selection.jpg
│ ├── gui-win-a-folder-locations.jpg
│ ├── gui-win-individual-files.jpg
│ ├── gui-win-invoke-completion.jpg
│ ├── firstrun-mac-securitygeneral.jpg
│ ├── gcp-credential-downloadjson.jpg
│ ├── gui-gv-credential-file-field.jpg
│ ├── gui-win-a-txt-file-location.jpg
│ ├── firstrun-mac-systempreference.jpg
│ ├── gui-gv-win-selectcredentialfile.jpg
│ ├── memespector-gui-screenshot-mac.png
│ └── gui-win-individual-file-locations.jpg
├── GetKeyFromClarifai.md
├── GetKeyFromMicrosoftAzure.md
├── FirstRun.md
└── GetKeyFromGoogleCloud.md
├── Memespector-GUI
├── Assets
│ └── Google-Noto-Emoji-eye.ico
├── App.axaml
├── nuget.config
├── App.axaml.cs
├── Program.cs
├── APIViewModels
│ ├── ClarifaiSettingsViewModel.cs
│ ├── OpenSourceSettingsViewModel.cs
│ ├── GoogleVisionSettingsViewModel.cs
│ └── MicrosoftAzureSettingsViewModel.cs
├── Memespector-GUI.csproj
├── MainWindow.axaml.cs
├── MemespectorConfig.cs
├── Utilities.cs
├── MainWindow.axaml
└── MainWindowViewModel.cs
├── CVClient
├── CVModel.cs
├── FlatResultImageInfo.cs
├── FlatResultClarifai.cs
├── FlatResultOpenSource.cs
├── InvocationBase.cs
├── ModelOptions
│ ├── OpenSourceModels.json
│ └── ClarifaiModels.json
├── CVClient.csproj
├── FlatResultAzure.cs
├── FlatResultGoogle.cs
├── CVClientManager.cs
├── CVClientCommon.cs
├── InvocationClarifai.cs
├── CVImageTask.cs
├── InvocationOpenSource.cs
├── InvocationAzure.cs
└── InvocationGoogle.cs
├── CITATION.cff
├── LICENSE.txt
├── ResearchCVClient.sln
├── .gitattributes
├── .gitignore
└── README.md
/doc/res/cl-api-key.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jason-chao/memespector-gui/HEAD/doc/res/cl-api-key.jpg
--------------------------------------------------------------------------------
/doc/res/ma-signin.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jason-chao/memespector-gui/HEAD/doc/res/ma-signin.jpg
--------------------------------------------------------------------------------
/doc/res/gcp-activate.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jason-chao/memespector-gui/HEAD/doc/res/gcp-activate.jpg
--------------------------------------------------------------------------------
/doc/res/firstrun-mac-exe.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jason-chao/memespector-gui/HEAD/doc/res/firstrun-mac-exe.jpg
--------------------------------------------------------------------------------
/doc/res/gcp-project-new.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jason-chao/memespector-gui/HEAD/doc/res/gcp-project-new.jpg
--------------------------------------------------------------------------------
/doc/res/gcp-signup-step1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jason-chao/memespector-gui/HEAD/doc/res/gcp-signup-step1.jpg
--------------------------------------------------------------------------------
/doc/res/gui-cl-api-key.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jason-chao/memespector-gui/HEAD/doc/res/gui-cl-api-key.jpg
--------------------------------------------------------------------------------
/doc/res/gui-os-endpoint.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jason-chao/memespector-gui/HEAD/doc/res/gui-os-endpoint.jpg
--------------------------------------------------------------------------------
/doc/res/gui-web-urls-txt.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jason-chao/memespector-gui/HEAD/doc/res/gui-web-urls-txt.jpg
--------------------------------------------------------------------------------
/doc/res/gui-win-a-folder.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jason-chao/memespector-gui/HEAD/doc/res/gui-win-a-folder.jpg
--------------------------------------------------------------------------------
/doc/res/gui-win-progress.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jason-chao/memespector-gui/HEAD/doc/res/gui-win-progress.jpg
--------------------------------------------------------------------------------
/doc/res/gui-win-settings.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jason-chao/memespector-gui/HEAD/doc/res/gui-win-settings.jpg
--------------------------------------------------------------------------------
/doc/res/gui-win-txt-file.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jason-chao/memespector-gui/HEAD/doc/res/gui-win-txt-file.jpg
--------------------------------------------------------------------------------
/doc/res/gcp-project-naming.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jason-chao/memespector-gui/HEAD/doc/res/gcp-project-naming.jpg
--------------------------------------------------------------------------------
/doc/res/gcp-signup-step2.1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jason-chao/memespector-gui/HEAD/doc/res/gcp-signup-step2.1.jpg
--------------------------------------------------------------------------------
/doc/res/gcp-signup-step2.2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jason-chao/memespector-gui/HEAD/doc/res/gcp-signup-step2.2.jpg
--------------------------------------------------------------------------------
/doc/res/ma-add-a-resource.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jason-chao/memespector-gui/HEAD/doc/res/ma-add-a-resource.jpg
--------------------------------------------------------------------------------
/doc/res/ma-cogserv-naming.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jason-chao/memespector-gui/HEAD/doc/res/ma-cogserv-naming.jpg
--------------------------------------------------------------------------------
/doc/res/ma-confirm-create.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jason-chao/memespector-gui/HEAD/doc/res/ma-confirm-create.jpg
--------------------------------------------------------------------------------
/doc/res/ma-go-to-resource.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jason-chao/memespector-gui/HEAD/doc/res/ma-go-to-resource.jpg
--------------------------------------------------------------------------------
/doc/res/ma-resource-search.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jason-chao/memespector-gui/HEAD/doc/res/ma-resource-search.jpg
--------------------------------------------------------------------------------
/doc/res/cl-application-naming.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jason-chao/memespector-gui/HEAD/doc/res/cl-application-naming.jpg
--------------------------------------------------------------------------------
/doc/res/cl-create-application.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jason-chao/memespector-gui/HEAD/doc/res/cl-create-application.jpg
--------------------------------------------------------------------------------
/doc/res/firstrun-windows-exe.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jason-chao/memespector-gui/HEAD/doc/res/firstrun-windows-exe.jpg
--------------------------------------------------------------------------------
/doc/res/gcp-credential-create.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jason-chao/memespector-gui/HEAD/doc/res/gcp-credential-create.jpg
--------------------------------------------------------------------------------
/doc/res/gcp-credential-done.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jason-chao/memespector-gui/HEAD/doc/res/gcp-credential-done.jpg
--------------------------------------------------------------------------------
/doc/res/gcp-credential-naming.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jason-chao/memespector-gui/HEAD/doc/res/gcp-credential-naming.jpg
--------------------------------------------------------------------------------
/doc/res/gcp-credential-role1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jason-chao/memespector-gui/HEAD/doc/res/gcp-credential-role1.jpg
--------------------------------------------------------------------------------
/doc/res/gcp-credential-role2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jason-chao/memespector-gui/HEAD/doc/res/gcp-credential-role2.jpg
--------------------------------------------------------------------------------
/doc/res/gcp-credential-role3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jason-chao/memespector-gui/HEAD/doc/res/gcp-credential-role3.jpg
--------------------------------------------------------------------------------
/doc/res/gcp-enableapi-enable.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jason-chao/memespector-gui/HEAD/doc/res/gcp-enableapi-enable.jpg
--------------------------------------------------------------------------------
/doc/res/gcp-enableapi-search.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jason-chao/memespector-gui/HEAD/doc/res/gcp-enableapi-search.jpg
--------------------------------------------------------------------------------
/doc/res/gcp-enableapi-select.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jason-chao/memespector-gui/HEAD/doc/res/gcp-enableapi-select.jpg
--------------------------------------------------------------------------------
/doc/res/gui-cl-api-key-field.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jason-chao/memespector-gui/HEAD/doc/res/gui-cl-api-key-field.jpg
--------------------------------------------------------------------------------
/doc/res/gui-ma-key-endpoint.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jason-chao/memespector-gui/HEAD/doc/res/gui-ma-key-endpoint.jpg
--------------------------------------------------------------------------------
/doc/res/gui-win-invokebutton.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jason-chao/memespector-gui/HEAD/doc/res/gui-win-invokebutton.jpg
--------------------------------------------------------------------------------
/doc/res/gui-win-outputfiles.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jason-chao/memespector-gui/HEAD/doc/res/gui-win-outputfiles.jpg
--------------------------------------------------------------------------------
/doc/res/gui-win-web-locations.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jason-chao/memespector-gui/HEAD/doc/res/gui-win-web-locations.jpg
--------------------------------------------------------------------------------
/doc/res/ma-cognitive-create.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jason-chao/memespector-gui/HEAD/doc/res/ma-cognitive-create.jpg
--------------------------------------------------------------------------------
/doc/res/ma-keys-and-endpoint.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jason-chao/memespector-gui/HEAD/doc/res/ma-keys-and-endpoint.jpg
--------------------------------------------------------------------------------
/doc/res/ma-keys-endpoint-copy.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jason-chao/memespector-gui/HEAD/doc/res/ma-keys-endpoint-copy.jpg
--------------------------------------------------------------------------------
/doc/res/ma-region-selection.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jason-chao/memespector-gui/HEAD/doc/res/ma-region-selection.jpg
--------------------------------------------------------------------------------
/doc/res/ma-res-group-naming.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jason-chao/memespector-gui/HEAD/doc/res/ma-res-group-naming.jpg
--------------------------------------------------------------------------------
/doc/res/cl-application-created.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jason-chao/memespector-gui/HEAD/doc/res/cl-application-created.jpg
--------------------------------------------------------------------------------
/doc/res/firstrun-mac-cannotrun.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jason-chao/memespector-gui/HEAD/doc/res/firstrun-mac-cannotrun.jpg
--------------------------------------------------------------------------------
/doc/res/firstrun-mac-openanyway.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jason-chao/memespector-gui/HEAD/doc/res/firstrun-mac-openanyway.jpg
--------------------------------------------------------------------------------
/doc/res/gcp-credential-navigate.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jason-chao/memespector-gui/HEAD/doc/res/gcp-credential-navigate.jpg
--------------------------------------------------------------------------------
/doc/res/gcp-enableapi-navigate.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jason-chao/memespector-gui/HEAD/doc/res/gcp-enableapi-navigate.jpg
--------------------------------------------------------------------------------
/doc/res/gui-cl-model-selection.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jason-chao/memespector-gui/HEAD/doc/res/gui-cl-model-selection.jpg
--------------------------------------------------------------------------------
/doc/res/gui-gcp-credential-file.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jason-chao/memespector-gui/HEAD/doc/res/gui-gcp-credential-file.jpg
--------------------------------------------------------------------------------
/doc/res/gui-os-model-selection.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jason-chao/memespector-gui/HEAD/doc/res/gui-os-model-selection.jpg
--------------------------------------------------------------------------------
/doc/res/firstrun-mac-openrelease.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jason-chao/memespector-gui/HEAD/doc/res/firstrun-mac-openrelease.jpg
--------------------------------------------------------------------------------
/doc/res/firstrun-windows-moreinfo.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jason-chao/memespector-gui/HEAD/doc/res/firstrun-windows-moreinfo.jpg
--------------------------------------------------------------------------------
/doc/res/firstrun-windows-runanyway.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jason-chao/memespector-gui/HEAD/doc/res/firstrun-windows-runanyway.jpg
--------------------------------------------------------------------------------
/doc/res/gcp-credential-createjson.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jason-chao/memespector-gui/HEAD/doc/res/gcp-credential-createjson.jpg
--------------------------------------------------------------------------------
/doc/res/gcp-credential-createkey.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jason-chao/memespector-gui/HEAD/doc/res/gcp-credential-createkey.jpg
--------------------------------------------------------------------------------
/doc/res/gui-az-endpoint-key-fields.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jason-chao/memespector-gui/HEAD/doc/res/gui-az-endpoint-key-fields.jpg
--------------------------------------------------------------------------------
/doc/res/gui-az-feature-selection.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jason-chao/memespector-gui/HEAD/doc/res/gui-az-feature-selection.jpg
--------------------------------------------------------------------------------
/doc/res/gui-gv-feature-selection.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jason-chao/memespector-gui/HEAD/doc/res/gui-gv-feature-selection.jpg
--------------------------------------------------------------------------------
/doc/res/gui-win-a-folder-locations.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jason-chao/memespector-gui/HEAD/doc/res/gui-win-a-folder-locations.jpg
--------------------------------------------------------------------------------
/doc/res/gui-win-individual-files.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jason-chao/memespector-gui/HEAD/doc/res/gui-win-individual-files.jpg
--------------------------------------------------------------------------------
/doc/res/gui-win-invoke-completion.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jason-chao/memespector-gui/HEAD/doc/res/gui-win-invoke-completion.jpg
--------------------------------------------------------------------------------
/doc/res/firstrun-mac-securitygeneral.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jason-chao/memespector-gui/HEAD/doc/res/firstrun-mac-securitygeneral.jpg
--------------------------------------------------------------------------------
/doc/res/gcp-credential-downloadjson.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jason-chao/memespector-gui/HEAD/doc/res/gcp-credential-downloadjson.jpg
--------------------------------------------------------------------------------
/doc/res/gui-gv-credential-file-field.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jason-chao/memespector-gui/HEAD/doc/res/gui-gv-credential-file-field.jpg
--------------------------------------------------------------------------------
/doc/res/gui-win-a-txt-file-location.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jason-chao/memespector-gui/HEAD/doc/res/gui-win-a-txt-file-location.jpg
--------------------------------------------------------------------------------
/doc/res/firstrun-mac-systempreference.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jason-chao/memespector-gui/HEAD/doc/res/firstrun-mac-systempreference.jpg
--------------------------------------------------------------------------------
/doc/res/gui-gv-win-selectcredentialfile.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jason-chao/memespector-gui/HEAD/doc/res/gui-gv-win-selectcredentialfile.jpg
--------------------------------------------------------------------------------
/doc/res/memespector-gui-screenshot-mac.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jason-chao/memespector-gui/HEAD/doc/res/memespector-gui-screenshot-mac.png
--------------------------------------------------------------------------------
/doc/res/gui-win-individual-file-locations.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jason-chao/memespector-gui/HEAD/doc/res/gui-win-individual-file-locations.jpg
--------------------------------------------------------------------------------
/Memespector-GUI/Assets/Google-Noto-Emoji-eye.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jason-chao/memespector-gui/HEAD/Memespector-GUI/Assets/Google-Noto-Emoji-eye.ico
--------------------------------------------------------------------------------
/CVClient/CVModel.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 |
5 | namespace CVClient
6 | {
7 | public class CVModel
8 | {
9 | public string Title { get; set; }
10 | public string ModelId { get; set; }
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/Memespector-GUI/App.axaml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Memespector-GUI/nuget.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/CVClient/FlatResultImageInfo.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 |
5 | namespace CVClient
6 | {
7 | public class FlatResultImageInfo
8 | {
9 | public string Image_Location { get; set; } = string.Empty;
10 | public string Image_From { get; set; } = string.Empty;
11 | public string Image_BaseName { get; set; } = string.Empty;
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/CITATION.cff:
--------------------------------------------------------------------------------
1 | cff-version: 1.2.0
2 | message: "If you use this software, please cite it as below."
3 | authors:
4 | - family-names: "Chao"
5 | given-names: "Jason"
6 | orcid: "https://orcid.org/0000-0001-8005-1227"
7 | title: "Memespector-GUI: Graphical User Interface Client for Computer Vision APIs"
8 | version: 0.2.5
9 | doi: 10.5281/zenodo.7704877
10 | date-released: 2023-03-07
11 | url: "https://github.com/jason-chao/memespector-gui"
12 |
--------------------------------------------------------------------------------
/CVClient/FlatResultClarifai.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 |
5 | namespace CVClient
6 | {
7 | public class FlatResultClarifai
8 | {
9 | public bool Success { get; set; } = false;
10 |
11 | public string Concepts { get; set; } = string.Empty;
12 | public string Concept_Ids { get; set; } = string.Empty;
13 | public string Concept_Scores { get; set; } = string.Empty;
14 |
15 | public string Model { get; set; } = string.Empty;
16 | public string Error { get; set; } = string.Empty;
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/CVClient/FlatResultOpenSource.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 |
5 | namespace CVClient
6 | {
7 | public class FlatResultOpenSource
8 | {
9 | public bool Success { get; set; } = false;
10 |
11 | public string Labels { get; set; } = string.Empty;
12 | public string Label_Ids { get; set; } = string.Empty;
13 | public string Label_Scores { get; set; } = string.Empty;
14 |
15 | public string Model { get; set; } = string.Empty;
16 | public string Error { get; set; } = string.Empty;
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/CVClient/InvocationBase.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 | using Newtonsoft.Json;
5 | using Newtonsoft.Json.Converters;
6 |
7 | namespace CVClient
8 | {
9 | public class InvocationBase
10 | {
11 | [JsonIgnore]
12 | public CVClientCommon.VisionAPI? API { get; set; } = null;
13 |
14 | public string ImageLocation { get; set; }
15 |
16 | [JsonConverter(typeof(StringEnumConverter))]
17 | public CVClientCommon.LocationType ImageLocationType { get; set; }
18 |
19 | public DateTime? Processed { get; set; } = null;
20 |
21 | [JsonIgnore]
22 | public Exception ExceptionRasied { get; set; } = null;
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/Memespector-GUI/App.axaml.cs:
--------------------------------------------------------------------------------
1 | using Avalonia;
2 | using Avalonia.Controls.ApplicationLifetimes;
3 | using Avalonia.Markup.Xaml;
4 |
5 | namespace Memespector_GUI
6 | {
7 | public class App : Application
8 | {
9 | public override void Initialize()
10 | {
11 | AvaloniaXamlLoader.Load(this);
12 | }
13 |
14 | public override void OnFrameworkInitializationCompleted()
15 | {
16 | if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
17 | {
18 | desktop.MainWindow = new MainWindow() { DataContext = new MainWindowViewModel() };
19 | (desktop.MainWindow.DataContext as MainWindowViewModel).Parent = desktop.MainWindow;
20 | }
21 |
22 | base.OnFrameworkInitializationCompleted();
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/Memespector-GUI/Program.cs:
--------------------------------------------------------------------------------
1 | using Avalonia;
2 | using Avalonia.Controls;
3 | using Avalonia.ReactiveUI;
4 | using Avalonia.Controls.ApplicationLifetimes;
5 | using System;
6 |
7 | namespace Memespector_GUI
8 | {
9 | class Program
10 | {
11 | // Initialization code. Don't use any Avalonia, third-party APIs or any
12 | // SynchronizationContext-reliant code before AppMain is called: things aren't initialized
13 | // yet and stuff might break.
14 | public static void Main(string[] args) => BuildAvaloniaApp()
15 | .StartWithClassicDesktopLifetime(args);
16 |
17 | // Avalonia configuration, don't remove; also used by visual designer.
18 | public static AppBuilder BuildAvaloniaApp()
19 | => AppBuilder.Configure()
20 | .UsePlatformDetect()
21 | .LogToTrace()
22 | .UseReactiveUI();
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/doc/GetKeyFromClarifai.md:
--------------------------------------------------------------------------------
1 | # Signing up for Clarifai
2 |
3 | [Sign up for Clarifai](https://portal.clarifai.com/signup) if you do not have an account.
4 |
5 | # Obtaining an API key
6 |
7 | After [logging on to Clarifai](https://portal.clarifai.com/login), click `CREATE APPLICATION` on the top right.
8 |
9 | 
10 |
11 | Give the application a unique id. (The id must consist of one or more alphanumeric strings separated by hyphens or underscores. For example, 'id1' and 'id-1' are okay, but '-id1', 'id--1', and 'id_1!' are not okay.) Then, click `CREATE`.
12 |
13 | 
14 |
15 | Click the tile showing application you created just now.
16 |
17 | 
18 |
19 | Copy the `API Key` by clicking the clipboard 📋. Then, paste it to the `API key` box for Clarifai in Memespector GUI.
20 |
21 | 
22 |
23 | 
24 |
--------------------------------------------------------------------------------
/Memespector-GUI/APIViewModels/ClarifaiSettingsViewModel.cs:
--------------------------------------------------------------------------------
1 | using ReactiveUI;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 |
6 | using CVClient;
7 |
8 | namespace Memespector_GUI.APIViewModels
9 | {
10 | public class ClarifaiSettingsViewModel : ReactiveObject
11 | {
12 | public ClarifaiSettingsViewModel() { Model = AvailableModels.First(); }
13 |
14 | private string apiKey = Utilities.MemespectorConfig.ClarifaiAPIKey;
15 | public string APIKey { get => apiKey; set => this.RaiseAndSetIfChanged(ref apiKey, value); }
16 |
17 | private string model = string.Empty;
18 | public string Model { get => model; set => this.RaiseAndSetIfChanged(ref model, value); }
19 |
20 | public List AvailableModels
21 | {
22 | get
23 | {
24 | return new List(CVClientCommon.GetAPIAvailableModels(CVClientCommon.VisionAPI.Clarifai).Keys);
25 | }
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/Memespector-GUI/APIViewModels/OpenSourceSettingsViewModel.cs:
--------------------------------------------------------------------------------
1 | using CVClient;
2 | using ReactiveUI;
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Linq;
6 |
7 | namespace Memespector_GUI.APIViewModels
8 | {
9 | public class OpenSourceSettingsViewModel : ReactiveObject
10 | {
11 | public OpenSourceSettingsViewModel() { Model = AvailableModels.First(); }
12 |
13 | private string endpoint = Utilities.MemespectorConfig.OpenSourceEndpoint;
14 | public string Endpoint { get => endpoint; set => this.RaiseAndSetIfChanged(ref endpoint, value); }
15 |
16 | private string model = string.Empty;
17 | public string Model { get => model; set => this.RaiseAndSetIfChanged(ref model, value); }
18 |
19 | public List AvailableModels
20 | {
21 | get
22 | {
23 | return new List(CVClientCommon.GetAPIAvailableModels(CVClientCommon.VisionAPI.OpenSource).Keys);
24 | }
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | Copyright 2021 Teng Hei Jason CHAO
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4 |
5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6 |
7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
8 |
--------------------------------------------------------------------------------
/CVClient/ModelOptions/OpenSourceModels.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "Title": "VGG16 (ImageNet)",
4 | "ModelId": "VGG16"
5 | },
6 | {
7 | "Title": "VGG19 (ImageNet)",
8 | "ModelId": "VGG19"
9 | },
10 | {
11 | "Title": "ResNet50 (ImageNet)",
12 | "ModelId": "ResNet50"
13 | },
14 | {
15 | "Title": "ResNet152V2 (ImageNet)",
16 | "ModelId": "ResNet152V2"
17 | },
18 | {
19 | "Title": "Xception (ImageNet)",
20 | "ModelId": "Xception"
21 | },
22 | {
23 | "Title": "InceptionV3 (ImageNet)",
24 | "ModelId": "InceptionV3"
25 | },
26 | {
27 | "Title": "InceptionResNetV2 (ImageNet)",
28 | "ModelId": "InceptionResNetV2"
29 | },
30 | {
31 | "Title": "MobileNetV2 (ImageNet)",
32 | "ModelId": "MobileNetV2"
33 | },
34 | {
35 | "Title": "EfficientNetB7 (ImageNet)",
36 | "ModelId": "EfficientNetB7"
37 | },
38 | {
39 | "Title": "NASNetLarge (ImageNet)",
40 | "ModelId": "NASNetLarge"
41 | },
42 | {
43 | "Title": "DenseNet201 (ImageNet)",
44 | "ModelId": "DenseNet201"
45 | }
46 | ]
--------------------------------------------------------------------------------
/CVClient/ModelOptions/ClarifaiModels.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "Title": "General",
4 | "ModelId": "aaa03c23b3724a16a56b629203edc62c"
5 | },
6 | {
7 | "Title": "Apparel",
8 | "ModelId": "e0be3b9d6a454f0493ac3a30784001ff"
9 | },
10 | {
11 | "Title": "Celebrity",
12 | "ModelId": "e466caa0619f444ab97497640cefc4dc"
13 | },
14 | {
15 | "Title": "Color",
16 | "ModelId": "eeed0b6733a644cea07cf4c60f87ebb7"
17 | },
18 | {
19 | "Title": "Demographics",
20 | "ModelId": "c0c0ac362b03416da06ab3fa36fb58e3"
21 | },
22 | {
23 | "Title": "Face Detection",
24 | "ModelId": "a403429f2ddf4b49b307e318f00e528b"
25 | },
26 | {
27 | "Title": "Food",
28 | "ModelId": "bd367be194cf45149e75f01d59f77ba7"
29 | },
30 | {
31 | "Title": "Moderation",
32 | "ModelId": "d16f390eb32cad478c7ae150069bd2c6"
33 | },
34 | {
35 | "Title": "NSFW",
36 | "ModelId": "e9576d86d2004ed1a38ba0cf39ecb4b1"
37 | },
38 | {
39 | "Title": "Textures and Patterns",
40 | "ModelId": "fbefb47f9fdb410e8ce14f24f54b47ff"
41 | },
42 | {
43 | "Title": "Travel",
44 | "ModelId": "eee28c313d69466f836ab83287a54ed9"
45 | }
46 | ]
47 |
--------------------------------------------------------------------------------
/CVClient/CVClient.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netstandard2.0
5 | Jason Chao
6 | 0.0.0.1
7 | 0.0.0.1
8 | 0.2.5
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/CVClient/FlatResultAzure.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 |
5 | namespace CVClient
6 | {
7 | public class FlatResultAzure
8 | {
9 | public bool Success { get; set; } = false;
10 |
11 | public string Categories { get; set; } = string.Empty;
12 | public string Categories_Scores { get; set; } = string.Empty;
13 | public bool? Adult_Adult { get; set; } = null;
14 | public bool? Adult_Racy { get; set; } = null;
15 | public bool? Adult_Gory { get; set; } = null;
16 | public string Adult_Adult_Score { get; set; } = null;
17 | public string Adult_Racy_Score { get; set; } = null;
18 | public string Adult_Gory_Score { get; set; } = null;
19 | public string Tags { get; set; } = string.Empty;
20 | public string Tag_Scores { get; set; } = string.Empty;
21 | public string Description_Tags { get; set; } = string.Empty;
22 | public string Description_Captions { get; set; } = string.Empty;
23 | public string Description_Caption_Scores { get; set; } = string.Empty;
24 | public string Face_Ages { get; set; } = string.Empty;
25 | public string Face_Genders { get; set; } = string.Empty;
26 | public string Objects { get; set; } = string.Empty;
27 | public string Object_Scores { get; set; } = string.Empty;
28 | public string Brands { get; set; } = string.Empty;
29 | public string Brand_Scores { get; set; } = string.Empty;
30 |
31 | public string Error { get; set; } = string.Empty;
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/Memespector-GUI/Memespector-GUI.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | WinExe
4 | netcoreapp3.1
5 | enable
6 | Assets\Google-Noto-Emoji-eye.ico
7 | Jason Chao
8 | 0.0.2.4
9 | 0.0.2.4
10 | 0.2.5
11 | https://github.com/jason-chao/memespector-gui
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 | true
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/Memespector-GUI/MainWindow.axaml.cs:
--------------------------------------------------------------------------------
1 | using Avalonia;
2 | using Avalonia.Controls;
3 | using Avalonia.Markup.Xaml;
4 |
5 | namespace Memespector_GUI
6 | {
7 | public class MainWindow : Window
8 | {
9 | public MainWindow()
10 | {
11 | InitializeComponent();
12 | #if DEBUG
13 | this.AttachDevTools();
14 | #endif
15 | }
16 |
17 | private void InitializeComponent()
18 | {
19 | AvaloniaXamlLoader.Load(this);
20 | this.Closing += MainWindow_Closing;
21 | }
22 |
23 | private async void MainWindow_Closing(object? sender, System.ComponentModel.CancelEventArgs e)
24 | {
25 | var viewModel = this.DataContext as MainWindowViewModel;
26 | if (viewModel != null)
27 | {
28 | if (viewModel.IsInvocationInProgress)
29 | {
30 | e.Cancel = true;
31 | var dialogChoice = await Utilities.ShowOkCancelDialog("Invocation in progress", $"The images are being processed. Some of the images will not be processed if you quit the application now.{System.Environment.NewLine + System.Environment.NewLine}Press \"OK\" if you are sure want to quit the application.", this);
32 | if (dialogChoice == MessageBox.Avalonia.Enums.ButtonResult.Ok)
33 | {
34 | System.Environment.Exit(0);
35 | }
36 | }
37 | }
38 | }
39 |
40 | private void resetComboBoxSelectedIndex(object sender, SelectionChangedEventArgs args)
41 | {
42 | (sender as ComboBox).SelectedItem = null;
43 | }
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/ResearchCVClient.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 16
4 | VisualStudioVersion = 16.0.30907.101
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CVClient", "CVClient\CVClient.csproj", "{C07EA061-293F-4BCB-A096-66788CC5CA54}"
7 | EndProject
8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Memespector-GUI", "Memespector-GUI\Memespector-GUI.csproj", "{9534D1DE-7918-44E8-BECA-CA68726194D8}"
9 | EndProject
10 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{F8DD9269-79F6-4ABC-B516-CAE5D5530878}"
11 | ProjectSection(SolutionItems) = preProject
12 | LICENSE.txt = LICENSE.txt
13 | README.md = README.md
14 | EndProjectSection
15 | EndProject
16 | Global
17 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
18 | Debug|Any CPU = Debug|Any CPU
19 | Release|Any CPU = Release|Any CPU
20 | EndGlobalSection
21 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
22 | {C07EA061-293F-4BCB-A096-66788CC5CA54}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
23 | {C07EA061-293F-4BCB-A096-66788CC5CA54}.Debug|Any CPU.Build.0 = Debug|Any CPU
24 | {C07EA061-293F-4BCB-A096-66788CC5CA54}.Release|Any CPU.ActiveCfg = Release|Any CPU
25 | {C07EA061-293F-4BCB-A096-66788CC5CA54}.Release|Any CPU.Build.0 = Release|Any CPU
26 | {9534D1DE-7918-44E8-BECA-CA68726194D8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
27 | {9534D1DE-7918-44E8-BECA-CA68726194D8}.Debug|Any CPU.Build.0 = Debug|Any CPU
28 | {9534D1DE-7918-44E8-BECA-CA68726194D8}.Release|Any CPU.ActiveCfg = Release|Any CPU
29 | {9534D1DE-7918-44E8-BECA-CA68726194D8}.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 = {7BCD8DE3-9032-4AAC-9D43-EF0CD7E980C7}
36 | EndGlobalSection
37 | EndGlobal
38 |
--------------------------------------------------------------------------------
/doc/GetKeyFromMicrosoftAzure.md:
--------------------------------------------------------------------------------
1 | # Signing up for Azure
2 |
3 | Log on to [Microsoft Azure Portal](https://console.cloud.google.com) using your Gmail account. Click `Portal` on the top right corner. Then, sign in using your Microsoft account which is usually a hotmail.com or outlook.com email address.
4 |
5 | 
6 |
7 | Microsoft may ask you to start a free trial subscription with some credits. In the process, Microsoft may for your credit card details. You must provide Microsoft with a valid debit / credit card. Otherwise, you will not be able to use Microsoft Azure. From developers' experience, Microsoft will check if your card is valid but will not charge on your card when you sign up.
8 |
9 | # Obtaining a service endpoint and a subscription key
10 |
11 | After signing up for Microsoft Azure, you need to create Cognitive Services before you can use Microsoft Azure Cognitive Services Computer Vision.
12 |
13 | Click `Create a resource`.
14 |
15 | 
16 |
17 | Type `cognitive` in the search bar. `Cognitive Services` will show up in the auto-complete list. Click `Cognitive Services` on the list.
18 |
19 | 
20 |
21 | Click `Create`.
22 |
23 | 
24 |
25 | You will need to create a `Resource Group` if you do not have one. In this case, click `Create new` to create one. Give a name to the resource group, for example, "my-research-resource-group". Then click `OK`.
26 |
27 | 
28 |
29 | Select a `Region` as close as possible to your physical location.
30 |
31 | 
32 |
33 | Give a `Name` to your Cognitive Services. Select a `Pricing Tier`. Check the checkbox to signify your consent to the terms. Then click `Review + create`.
34 |
35 | 
36 |
37 | Click `Create`.
38 |
39 | 
40 |
41 | Click `Go to resource`.
42 |
43 | 
44 |
45 | Click `Keys and Endpoint` on the left.
46 |
47 | 
48 |
49 | Copy `Endpoint` and paste it to the `Endpoint` box for Microsoft Azure in Memespector-GUI. Also, copy `KEY1` (or `KEY2` if you like) and paste it to the `Subscription Key` box for Microsoft Azure in Memespector-GUI.
50 |
51 | 
52 |
53 | 
54 |
--------------------------------------------------------------------------------
/CVClient/FlatResultGoogle.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 |
5 | namespace CVClient
6 | {
7 | public class FlatResultGoogle
8 | {
9 | public bool Success { get; set; } = false;
10 |
11 | public string Safe_Adult { get; set; } = string.Empty;
12 | public string Safe_Spoof { get; set; } = string.Empty;
13 | public string Safe_Medical { get; set; } = string.Empty;
14 | public string Safe_Violence { get; set; } = string.Empty;
15 | public string Safe_Racy { get; set; } = string.Empty;
16 |
17 | public string Face_Joy { get; set; } = string.Empty;
18 | public string Face_Sorrow { get; set; } = string.Empty;
19 | public string Face_Anger { get; set; } = string.Empty;
20 | public string Face_Surprise { get; set; } = string.Empty;
21 | public string Face_UnderExposed { get; set; } = string.Empty;
22 | public string Face_Blurred { get; set; } = string.Empty;
23 | public string Face_Headwear { get; set; } = string.Empty;
24 | public string Face_Score { get; set; } = string.Empty;
25 |
26 | public string Label_Descriptions { get; set; } = string.Empty;
27 | public string Label_Ids { get; set; } = string.Empty;
28 | public string Label_Scores { get; set; } = string.Empty;
29 |
30 | public string Logo_Descriptions { get; set; } = string.Empty;
31 | public string Logo_Ids { get; set; } = string.Empty;
32 | public string Logo_Scores { get; set; } = string.Empty;
33 |
34 | public string Landmark_Descriptions { get; set; } = string.Empty;
35 | public string Landmark_Ids { get; set; } = string.Empty;
36 | public string Landmark_Scores { get; set; } = string.Empty;
37 |
38 | public string Text { get; set; } = string.Empty;
39 |
40 | public string Web_Entity_Descriptions { get; set; } = string.Empty;
41 | public string Web_Entity_Ids { get; set; } = string.Empty;
42 | public string Web_Entity_Scores { get; set; } = string.Empty;
43 | public string Web_BestGuessLabels { get; set; } = string.Empty;
44 |
45 | public string Web_FullMatchingImages { get; set; } = string.Empty;
46 | public string Web_PagesWithFullMatchingImages { get; set; } = string.Empty;
47 | public string Web_PartialMatchingImages { get; set; } = string.Empty;
48 | public string Web_VisuallySimilarImages { get; set; } = string.Empty;
49 |
50 | public string Error { get; set; } = string.Empty;
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/doc/FirstRun.md:
--------------------------------------------------------------------------------
1 | # Allowing Memespector-GUI to run at first launch
2 |
3 | When you open Memespector-GUI for the first time on Windows or Mac, the operating system may decline to run Memespector-GUI because of tightened security measures. You have to take some extra steps to ask Windows or Mac to allow Memespector-GUI to run on your computer.
4 |
5 | Usually, you will only need to take these extra steps once. Next time, Memespector-GUI should run without further hindrance.
6 |
7 | ## Operating system-specific instructions
8 | * [Windows](#on-windows)
9 | * [Mac](#on-mac)
10 |
11 | ## On Windows
12 |
13 | After extracting the zip file, you will see the file `Memespector-GUI.exe` with an eye's icon. (Note: The file extension .exe may be hidden depending on the settings.) Double click on it to open Memespector-GUI.
14 |
15 | 
16 |
17 | If you see a window telling you not to run the app, click `More info`.
18 |
19 | 
20 |
21 | You will see that a new button will appear at the bottom. Click `Run anyway`. Then, Memespector-GUI should open in a few seconds.
22 |
23 | 
24 |
25 |
26 | ## On Mac
27 |
28 | Safari may have extracted the zip file automatically for you after downloading it. Open the `Memespector-GUI-macOS` folder and click `Memespector-GUI` inside.
29 |
30 | 
31 |
32 | If you see a window asking that are you sure you want to open Memespector-GUI, click `Open`. Then, Memespector-GUI should open in a few seconds.
33 |
34 | 
35 |
36 | However, in some cases, you may not see the window above but instead encounter another window without an Open button. In this case, see the additional steps as follows.
37 |
38 | In case you see a window telling you that Memespector-GUI cannot be opened, click `Ok`.
39 |
40 | 
41 |
42 | Open [System Preferences](https://support.apple.com/guide/macbook-pro/system-preferences-apda966cb8af/mac) on your Mac. Click `Security & Privacy`.
43 |
44 | 
45 |
46 | Navigate to the `General` tab. You should be able to see `"Memespector-GUI" was blocked from use because it is not from an identified developer` at the bottom of the tab. Click `Open Anyway` to the right of that message.
47 |
48 | 
49 |
50 | Click `Open`.
51 |
52 | 
53 |
54 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | ###############################################################################
2 | # Set default behavior to automatically normalize line endings.
3 | ###############################################################################
4 | * text=auto
5 |
6 | ###############################################################################
7 | # Set default behavior for command prompt diff.
8 | #
9 | # This is need for earlier builds of msysgit that does not have it on by
10 | # default for csharp files.
11 | # Note: This is only used by command line
12 | ###############################################################################
13 | #*.cs diff=csharp
14 |
15 | ###############################################################################
16 | # Set the merge driver for project and solution files
17 | #
18 | # Merging from the command prompt will add diff markers to the files if there
19 | # are conflicts (Merging from VS is not affected by the settings below, in VS
20 | # the diff markers are never inserted). Diff markers may cause the following
21 | # file extensions to fail to load in VS. An alternative would be to treat
22 | # these files as binary and thus will always conflict and require user
23 | # intervention with every merge. To do so, just uncomment the entries below
24 | ###############################################################################
25 | #*.sln merge=binary
26 | #*.csproj merge=binary
27 | #*.vbproj merge=binary
28 | #*.vcxproj merge=binary
29 | #*.vcproj merge=binary
30 | #*.dbproj merge=binary
31 | #*.fsproj merge=binary
32 | #*.lsproj merge=binary
33 | #*.wixproj merge=binary
34 | #*.modelproj merge=binary
35 | #*.sqlproj merge=binary
36 | #*.wwaproj merge=binary
37 |
38 | ###############################################################################
39 | # behavior for image files
40 | #
41 | # image files are treated as binary by default.
42 | ###############################################################################
43 | #*.jpg binary
44 | #*.png binary
45 | #*.gif binary
46 |
47 | ###############################################################################
48 | # diff behavior for common document formats
49 | #
50 | # Convert binary document formats to text before diffing them. This feature
51 | # is only available from the command line. Turn it on by uncommenting the
52 | # entries below.
53 | ###############################################################################
54 | #*.doc diff=astextplain
55 | #*.DOC diff=astextplain
56 | #*.docx diff=astextplain
57 | #*.DOCX diff=astextplain
58 | #*.dot diff=astextplain
59 | #*.DOT diff=astextplain
60 | #*.pdf diff=astextplain
61 | #*.PDF diff=astextplain
62 | #*.rtf diff=astextplain
63 | #*.RTF diff=astextplain
64 |
--------------------------------------------------------------------------------
/Memespector-GUI/MemespectorConfig.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 |
5 | namespace Memespector_GUI
6 | {
7 |
8 | public class MemespectorConfig
9 | {
10 | public bool SelectedGoogleVision { get; set; } = false;
11 | public bool SelectedMicrosoftAzure { get; set; } = false;
12 | public bool SelectedClarifai { get; set; } = false;
13 | public bool SelectedOpenSource { get; set; } = false;
14 |
15 | public string GoogleCloudCredentialFileLocation { get; set; } = string.Empty;
16 | public string MicrosoftAzureSubscriptionKey { get; set; } = string.Empty;
17 | public string MicrosoftAzureEndpoint { get; set; } = string.Empty;
18 | public string ClarifaiAPIKey { get; set; } = string.Empty;
19 | public string OpenSourceEndpoint { get; set; } = "https://europe-west1-digital-methods-resources.cloudfunctions.net/classify_image_v1";
20 |
21 | public int MaxConcurrentImageTasks { get; set; } = Environment.ProcessorCount;
22 |
23 | public GoogleFeatureMaxResults GoogleVision_MaxResults { get; set; } = new GoogleFeatureMaxResults();
24 | public GoogleFeatureMinScores GoogleVision_CSV_MinScores { get; set; } = new GoogleFeatureMinScores();
25 | public MicrosoftFeatureMinScores MicrosoftAzure_CSV_MinScores { get; set; } = new MicrosoftFeatureMinScores();
26 |
27 | public int MinUIProgressUpdateIntervalInMilliseconds { get; set; } = 250;
28 | public int MinWriteResultsIntervalInSeconds { get; set; } = 15;
29 | public int MaxUIImageSourceLinesToDisplay { get; set; } = 256;
30 |
31 | public float ClarifaiMinScore { get; set; } = 0;
32 | public float OpenSourceMinScore { get; set; } = 0;
33 | public int OpenSourceMaxResults { get; set; } = 10;
34 |
35 | public class MicrosoftFeatureMinScores
36 | {
37 | public float Categories { get; set; } = 0;
38 | public float Description { get; set; } = 0;
39 | public float Tags { get; set; } = 0;
40 | public float Brands { get; set; } = 0;
41 | public float Objects { get; set; } = 0;
42 | }
43 |
44 | public class GoogleFeatureMaxResults
45 | {
46 | public int Face { get; set; } = 0;
47 | public int Label { get; set; } = 0;
48 | public int Web { get; set; } = 0;
49 | public int Landmark { get; set; } = 0;
50 | public int Logo { get; set; } = 0;
51 | }
52 |
53 | public class GoogleFeatureMinScores
54 | {
55 | public float Label { get; set; } = 0;
56 | public float Web { get; set; } = 0;
57 | public float Landmark { get; set; } = 0;
58 | public float Logo { get; set; } = 0;
59 | public float Face { get; set; } = 0;
60 | }
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/Memespector-GUI/APIViewModels/GoogleVisionSettingsViewModel.cs:
--------------------------------------------------------------------------------
1 | using ReactiveUI;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 |
6 | namespace Memespector_GUI.APIViewModels
7 | {
8 | public class GoogleVisionSettingsViewModel : ReactiveObject
9 | {
10 | public GoogleVisionSettingsViewModel()
11 | {
12 | this.WhenAnyValue(x => x.Detection_Safety, x => x.Detection_Face, x => x.Detection_Label, x => x.Detection_Web, x => x.Detection_Text, x => x.Detection_Landmark, x => x.Detection_Logo).Subscribe(_ => updateFeatureCount());
13 | this.WhenAnyValue(x => x.SelectedFeatureCount).Subscribe(x => { SelectedFeatureCountText = string.Format("{0} selected", x); });
14 | updateFeatureCount();
15 | }
16 |
17 | private string gcsCredentialFileLocation = Utilities.MemespectorConfig.GoogleCloudCredentialFileLocation;
18 | public string CredentialFileLocation { get => gcsCredentialFileLocation; set => this.RaiseAndSetIfChanged(ref gcsCredentialFileLocation, value); }
19 |
20 | private bool detection_Safety = true;
21 | public bool Detection_Safety { get => detection_Safety; set => this.RaiseAndSetIfChanged(ref detection_Safety, value); }
22 | private bool detection_Face = true;
23 | public bool Detection_Face { get => detection_Face; set => this.RaiseAndSetIfChanged(ref detection_Face, value); }
24 | private bool detection_Label = true;
25 | public bool Detection_Label { get => detection_Label; set => this.RaiseAndSetIfChanged(ref detection_Label, value); }
26 | private bool detection_Web = true;
27 | public bool Detection_Web { get => detection_Web; set => this.RaiseAndSetIfChanged(ref detection_Web, value); }
28 | private bool detection_Text = true;
29 | public bool Detection_Text { get => detection_Text; set => this.RaiseAndSetIfChanged(ref detection_Text, value); }
30 | private bool detection_Landmark = true;
31 | public bool Detection_Landmark { get => detection_Landmark; set => this.RaiseAndSetIfChanged(ref detection_Landmark, value); }
32 | private bool detection_Logo = true;
33 | public bool Detection_Logo { get => detection_Logo; set => this.RaiseAndSetIfChanged(ref detection_Logo, value); }
34 |
35 | private int selectedFeatureCount = 0;
36 | public int SelectedFeatureCount { get => selectedFeatureCount; set => this.RaiseAndSetIfChanged(ref selectedFeatureCount, value); }
37 |
38 | private string selectedFeatureCountText = "Please select ...";
39 | public string SelectedFeatureCountText { get => selectedFeatureCountText; set => this.RaiseAndSetIfChanged(ref selectedFeatureCountText, value); }
40 |
41 | private void updateFeatureCount()
42 | {
43 | SelectedFeatureCount = (new bool[] { detection_Safety, detection_Face, detection_Label, detection_Web, detection_Text, detection_Landmark, detection_Logo }).Where(f => f).Count();
44 | }
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/Memespector-GUI/APIViewModels/MicrosoftAzureSettingsViewModel.cs:
--------------------------------------------------------------------------------
1 | using ReactiveUI;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Text;
5 | using System.Linq;
6 |
7 | namespace Memespector_GUI.APIViewModels
8 | {
9 | public class MicrosoftAzureSettingsViewModel : ReactiveObject
10 | {
11 | public MicrosoftAzureSettingsViewModel()
12 | {
13 | this.WhenAnyValue(x => x.Detection_Adult, x => x.Detection_Brands, x => x.Detection_Categories, x => x.Detection_Description, x => x.Detection_Faces, x => x.Detection_Objects, x => x.Detection_Tags).Subscribe(_ => updateFeatureCount());
14 | this.WhenAnyValue(x => x.SelectedFeatureCount).Subscribe(x => { SelectedFeatureCountText = string.Format("{0} selected", x); });
15 | updateFeatureCount();
16 | }
17 | private string endpoint = Utilities.MemespectorConfig.MicrosoftAzureEndpoint;
18 | public string Endpoint { get => endpoint; set => this.RaiseAndSetIfChanged(ref endpoint, value); }
19 |
20 | private string subscriptionKey = Utilities.MemespectorConfig.MicrosoftAzureSubscriptionKey;
21 | public string SubscriptionKey { get => subscriptionKey; set => this.RaiseAndSetIfChanged(ref subscriptionKey, value); }
22 |
23 | private bool detection_Adult = true;
24 | public bool Detection_Adult { get => detection_Adult; set => this.RaiseAndSetIfChanged(ref detection_Adult, value); }
25 |
26 | private bool detection_Brands = true;
27 | public bool Detection_Brands { get => detection_Brands; set => this.RaiseAndSetIfChanged(ref detection_Brands, value); }
28 |
29 | private bool detection_Categories = true;
30 | public bool Detection_Categories { get => detection_Categories; set => this.RaiseAndSetIfChanged(ref detection_Categories, value); }
31 |
32 | private bool detection_Description = true;
33 | public bool Detection_Description { get => detection_Description; set => this.RaiseAndSetIfChanged(ref detection_Description, value); }
34 |
35 | private bool detection_Faces = true;
36 | public bool Detection_Faces { get => detection_Faces; set => this.RaiseAndSetIfChanged(ref detection_Faces, value); }
37 |
38 | private bool detection_Objects = true;
39 | public bool Detection_Objects { get => detection_Objects; set => this.RaiseAndSetIfChanged(ref detection_Objects, value); }
40 |
41 | private bool detection_Tags = true;
42 | public bool Detection_Tags { get => detection_Tags; set => this.RaiseAndSetIfChanged(ref detection_Tags, value); }
43 |
44 | private int selectedFeatureCount = 0;
45 | public int SelectedFeatureCount { get => selectedFeatureCount; set => this.RaiseAndSetIfChanged(ref selectedFeatureCount, value); }
46 |
47 | private string selectedFeatureCountText = "Please select ...";
48 | public string SelectedFeatureCountText { get => selectedFeatureCountText; set => this.RaiseAndSetIfChanged(ref selectedFeatureCountText, value); }
49 |
50 | private void updateFeatureCount()
51 | {
52 | SelectedFeatureCount = (new bool[] { detection_Adult, detection_Brands, detection_Categories, detection_Description, detection_Faces, detection_Objects, detection_Tags }).Where(f => f).Count();
53 | }
54 |
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/CVClient/CVClientManager.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Concurrent;
3 | using System.Collections.Generic;
4 | using System.IO;
5 | using System.Linq;
6 | using System.Threading.Tasks;
7 |
8 | namespace CVClient
9 | {
10 | public class CVClientManager
11 | {
12 | public CVClientManager()
13 | {
14 | }
15 |
16 | public int parallelCVImageTasksToRun = Environment.ProcessorCount;
17 | public int ParallelCVImageTasksToRun { get => parallelCVImageTasksToRun; set { if (value > 0) parallelCVImageTasksToRun = value; } }
18 |
19 | private string googleCloudCredentialsFilePath = string.Empty;
20 | private string microsoftAzureEndpoint = string.Empty;
21 | private string microsoftAzureSubscriptionKey = string.Empty;
22 | private string clarifaiApiKey = string.Empty;
23 | private string openSourceApiEndpoint = string.Empty;
24 |
25 | public string GoogleCloudCredentialsFile
26 | {
27 | get => googleCloudCredentialsFilePath;
28 | set { googleCloudCredentialsFilePath = value; Environment.SetEnvironmentVariable("GOOGLE_APPLICATION_CREDENTIALS", value); }
29 | }
30 |
31 | public string MicrosoftAzureEndpoint
32 | {
33 | get => microsoftAzureEndpoint;
34 | set { microsoftAzureEndpoint = value; Environment.SetEnvironmentVariable("MICROSOFT_AZURE_CV_ENDPOINT", value); }
35 | }
36 |
37 | public string MicrosoftAzureSubscriptionKey
38 | {
39 | get => microsoftAzureSubscriptionKey;
40 | set { microsoftAzureSubscriptionKey = value; Environment.SetEnvironmentVariable("MICROSOFT_AZURE_CV_KEY", value); }
41 | }
42 |
43 | public string ClarifaiApiKey
44 | {
45 | get => clarifaiApiKey;
46 | set { clarifaiApiKey = value; Environment.SetEnvironmentVariable("CLARIFAI_API_KEY", value); }
47 | }
48 |
49 | public string OpenSourceApiEndpoint
50 | {
51 | get => openSourceApiEndpoint;
52 | set { openSourceApiEndpoint = value; Environment.SetEnvironmentVariable("OPEN_SOURCE_CV_ENDPOINT", value); }
53 | }
54 |
55 |
56 | public bool HasGoogleCloudCredentialsFile()
57 | {
58 | if (string.IsNullOrEmpty(googleCloudCredentialsFilePath))
59 | return false;
60 | if (!File.Exists(googleCloudCredentialsFilePath))
61 | return false;
62 | return true;
63 | }
64 |
65 | public async Task RunTasks(IEnumerable cvImageTasks)
66 | {
67 | var queue = new ConcurrentQueue(Enumerable.Range(0, cvImageTasks.Count()));
68 |
69 | List tasks = new List();
70 |
71 | for (int n = 0; n < parallelCVImageTasksToRun; n++)
72 | {
73 | tasks.Add(Task.Run(() =>
74 | {
75 | while (queue.TryDequeue(out int gvTaskIndex))
76 | {
77 | Task.Run(cvImageTasks.Skip(gvTaskIndex).First().AllAPIsAction).Wait();
78 | }
79 | }));
80 | }
81 |
82 | Task concurrentTasks = Task.WhenAll(tasks.ToArray());
83 |
84 | try
85 | {
86 | await concurrentTasks;
87 | }
88 | catch { }
89 | }
90 |
91 | }
92 | }
93 |
--------------------------------------------------------------------------------
/CVClient/CVClientCommon.cs:
--------------------------------------------------------------------------------
1 | // #define DEBUG_DONOTINVOKEREALAPI
2 |
3 | using System;
4 | using System.Collections.Generic;
5 | using System.IO;
6 | using Newtonsoft.Json;
7 | using ServiceStack.Text;
8 | using System.Linq;
9 | using System.Reflection;
10 |
11 | namespace CVClient
12 | {
13 | static public class CVClientCommon
14 | {
15 | public enum LocationType { Local, Uri }
16 |
17 | public enum VisionAPI { OpenSource, GoogleCloud, MicrosoftAzure, Clarifai }
18 |
19 | public static Dictionary GetAPIAvailableModels(VisionAPI api)
20 | {
21 | switch (api)
22 | {
23 | case VisionAPI.Clarifai:
24 | return readModelOptions("CVClient.ModelOptions.ClarifaiModels.json");
25 | case VisionAPI.OpenSource:
26 | return readModelOptions("CVClient.ModelOptions.OpenSourceModels.json");
27 | default:
28 | break;
29 | }
30 |
31 | return new Dictionary();
32 | }
33 |
34 | private static Dictionary readModelOptions(string resourceId)
35 | {
36 | var modelDict = new Dictionary();
37 | var cvModels = JsonConvert.DeserializeObject>(CVClientCommon.GetEmbeddedResourceText(resourceId));
38 | foreach (var cvModel in cvModels)
39 | {
40 | modelDict.Add(cvModel.Title, cvModel.ModelId);
41 | }
42 | return modelDict;
43 | }
44 |
45 | public static Stream GetEmbeddedResourceStream(string ResourceId)
46 | {
47 | var assembly = Assembly.GetExecutingAssembly();
48 | return assembly.GetManifestResourceStream(ResourceId);
49 | }
50 |
51 | public static string GetEmbeddedResourceText(string ResourceId)
52 | {
53 | Stream stream = GetEmbeddedResourceStream(ResourceId);
54 | using (StreamReader reader = new StreamReader(stream))
55 | {
56 | return reader.ReadToEnd();
57 | }
58 | }
59 |
60 | // Image formats supported by each API
61 | // Google Vision https://cloud.google.com/vision/docs/supported-files
62 | // Microsoft Azure https://docs.microsoft.com/en-us/azure/cognitive-services/computer-vision/overview
63 | // Clarifai https://docs.clarifai.com/portal-guide/data/supported-formats
64 | // The formats supported by all these APIs are JPEG, PNG, GIF and BMP
65 | static public List SupportedFormats = new List { "jpeg", "jpg", "png", "gif", "bmp" };
66 |
67 | static public bool Google_MayBeAValidCredentialFile(string filename)
68 | {
69 | try
70 | {
71 | var gcsCredentialFileProperties = new string[] { "project_id", "private_key_id", "private_key", "client_id" };
72 | var objectKeys = JsonObject.Parse(File.ReadAllText(filename)).Select(co => co.Key);
73 | return gcsCredentialFileProperties.All(p => objectKeys.Any(k => k.Contains(p)));
74 | }
75 | catch { }
76 | return false;
77 | }
78 |
79 | #if DEBUG_DONOTINVOKEREALAPI
80 | static public void SimulateAPIInvocation()
81 | {
82 | Random rand = new Random();
83 | System.Threading.Thread.Sleep(rand.Next(3000, 15000));
84 | throw new Exception("A deliberate exception to end the task for debugging purposes");
85 | }
86 | #endif
87 |
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/doc/GetKeyFromGoogleCloud.md:
--------------------------------------------------------------------------------
1 | # Signing up for Google Cloud
2 |
3 | If you have signed up for Google Cloud already, please jump to [Obtaining a credential key file](#obtaining-a-credential-key-file).
4 |
5 | Log on to [Google Cloud Platform](https://console.cloud.google.com) using your Gmail account. Click `Activate` on the top right corner or `TRY FOR FREE` in the centre.
6 |
7 | 
8 |
9 | Choose your country and check the box to agree to the service terms. Click `CONTINUE`.
10 |
11 | 
12 |
13 | Complete the form with your name, address and details of your payment card.
14 |
15 | 
16 |
17 | You must provide Google with a valid debit / credit card. Otherwise, you will not be able to use Google Vision API. From developers' experience, Google will check if your card is valid but will not charge on your card when you sign up.
18 |
19 | Click `START MY FREE TRIAL` when you are done.
20 |
21 | 
22 |
23 | # Obtaining a credential key file
24 |
25 | After signing up for Google Cloud, you need to [create a project](#create-a-project), [enable Vision API](#enable-vision-api) and [create a service account](#create-a-service-account) before you can use Google Vision API.
26 |
27 |
28 | ## Create a project
29 |
30 | Click `Select a project` on the top. Then Click `NEW PROJECT`.
31 |
32 | 
33 |
34 | Give a name to the project. Click `CREATE`.
35 |
36 | 
37 |
38 | ## Enable Vision API
39 |
40 | Click the sandwich menu button on the top left. Navigate to `API & Services` -> `Library`.
41 |
42 | 
43 |
44 | Click `Cloud Vision API`
45 |
46 | 
47 |
48 | If you are unable to find `Cloud Vision API` for some reason, please type `vision` into the search box. You should be able to see it now. Click `Cloud Vision API`.
49 |
50 | 
51 |
52 | Click `Enable`
53 |
54 | 
55 |
56 | ## Create a service account
57 |
58 | Click the sandwich menu button on the top left. Navigate to `IAM & Admin` -> `Service Accounts`.
59 |
60 | 
61 |
62 | Click `CREATE SERVICE ACCOUNT`.
63 |
64 | 
65 |
66 | Give a name to the account. It is usually a name for your computer as service accounts are usually issued to computers. Click `CREATE`.
67 |
68 | 
69 |
70 | Click `Select a role`
71 |
72 | 
73 |
74 | Navigate to `Basic` -> `Owner`.
75 |
76 | 
77 |
78 | Click `CONTINUE`.
79 |
80 | 
81 |
82 | Leave these fields blank. Click `DONE`.
83 |
84 | 
85 |
86 | Under `Actions` on the right-hand side, click the sandwich button on the row of the service account you created just now. Click `Create key`.
87 |
88 | 
89 |
90 | Select `JSON` for `Key type`. Click `CREATE`.
91 |
92 | 
93 |
94 | Your browser should automatically download a .json file or ask you to save a .json file. Please move the credential file to a safe location after downloading.
95 |
96 | 
97 |
98 | In Memespector GUI, click `Browse` and select the downloaded .json file as the `Credential file` for Google Vision.
99 |
100 | 
101 |
--------------------------------------------------------------------------------
/CVClient/InvocationClarifai.cs:
--------------------------------------------------------------------------------
1 | //#define DEBUG_DONOTINVOKEREALAPI
2 |
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Text;
6 | using System.Linq;
7 | using Newtonsoft.Json;
8 | using System.Threading.Tasks;
9 | using System.IO;
10 | using Clarifai.Api;
11 | using Clarifai.Channels;
12 | using Grpc.Core;
13 |
14 | namespace CVClient
15 | {
16 | public class InvocationClarifai : InvocationBase
17 | {
18 | public InvocationClarifai()
19 | {
20 | API = CVClientCommon.VisionAPI.Clarifai;
21 | }
22 |
23 | public MultiOutputResponse APIResponse { get; set; } = null;
24 |
25 | private string model = null;
26 |
27 | public string Model { get { return model; } set { if (ModelsAvailable.Keys.Contains(value)) model = value; else throw new Exception("This model does not exist"); } }
28 | public double FlatteningMinScore { get; set; } = 0;
29 |
30 | private Dictionary modelsAvailable = null;
31 |
32 | [JsonIgnore]
33 | public Dictionary ModelsAvailable
34 | {
35 | get
36 | {
37 |
38 | if (modelsAvailable == null)
39 | {
40 | modelsAvailable = CVClientCommon.GetAPIAvailableModels(API.Value);
41 | }
42 |
43 | return modelsAvailable;
44 | }
45 | }
46 |
47 | [JsonIgnore]
48 | public Action InvocationAction { get { if (action == null) action = GetAction(); return action; } }
49 |
50 | [JsonIgnore]
51 | private Action action = null;
52 |
53 | private Action GetAction()
54 | {
55 | return () =>
56 | {
57 | try
58 | {
59 |
60 | #if (!DEBUG_DONOTINVOKEREALAPI)
61 |
62 | var client = new V2.V2Client(ClarifaiChannel.Grpc());
63 |
64 | var metadata = new Metadata { { "Authorization", $"Key {Environment.GetEnvironmentVariable("CLARIFAI_API_KEY")}" } };
65 |
66 | PostModelOutputsRequest postModelOutputRequest = null;
67 |
68 | if (ImageLocationType == CVClientCommon.LocationType.Uri)
69 | {
70 | postModelOutputRequest = new PostModelOutputsRequest()
71 | {
72 | ModelId = "aaa03c23b3724a16a56b629203edc62c",
73 | Inputs = { new List() { new Input() { Data = new Data() { Image = new Image() { Url = ImageLocation } } } } }
74 | };
75 | }
76 | else if (ImageLocationType == CVClientCommon.LocationType.Local)
77 | {
78 | using (Stream imageStream = File.OpenRead(ImageLocation))
79 | {
80 | postModelOutputRequest = new PostModelOutputsRequest()
81 | {
82 | ModelId = "aaa03c23b3724a16a56b629203edc62c",
83 | Inputs = { new List() { new Input() { Data = new Data() { Image = new Image() { Base64 = Google.Protobuf.ByteString.FromStream(imageStream) } } } } }
84 | };
85 | }
86 | }
87 |
88 | APIResponse = client.PostModelOutputs(postModelOutputRequest, metadata);
89 |
90 | #endif
91 |
92 | #if DEBUG_DONOTINVOKEREALAPI
93 | CVClientCommon.SimulateAPIInvocation();
94 | #endif
95 | }
96 | catch (Exception ex)
97 | {
98 | ExceptionRasied = ex;
99 | }
100 | finally
101 | {
102 |
103 | Processed = DateTime.Now;
104 | }
105 | };
106 | }
107 |
108 | [JsonIgnore]
109 | public FlatResultClarifai FlatResult
110 | {
111 | get
112 | {
113 | var flatResult = new FlatResultClarifai();
114 |
115 | if (ExceptionRasied != null)
116 | {
117 | flatResult.Error = ExceptionRasied.Message;
118 | }
119 |
120 | if (APIResponse != null)
121 | {
122 | if (APIResponse.Status.Code == Clarifai.Api.Status.StatusCode.Success)
123 | {
124 | flatResult.Success = true;
125 | }
126 | }
127 |
128 | if (!flatResult.Success)
129 | return flatResult;
130 |
131 | var concepts = APIResponse.Outputs[0].Data.Concepts.Where(a => a.Value >= FlatteningMinScore);
132 |
133 | if (concepts.Any())
134 | {
135 | flatResult.Concepts = string.Join("; ", concepts.Select(l => l.Name));
136 | flatResult.Concept_Ids = string.Join("; ", concepts.Select(l => l.Id));
137 | flatResult.Concept_Scores = string.Join("; ", concepts.Select(l => l.Value));
138 | }
139 |
140 | flatResult.Model = model;
141 |
142 | return flatResult;
143 | }
144 | }
145 | }
146 | }
147 |
--------------------------------------------------------------------------------
/CVClient/CVImageTask.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.IO;
5 | using System.Threading.Tasks;
6 | using Newtonsoft.Json;
7 | using Newtonsoft.Json.Converters;
8 | using System.Dynamic;
9 |
10 | namespace CVClient
11 | {
12 | public class CVImageTask
13 | {
14 | public string ImageLocation { get; set; }
15 |
16 | [JsonConverter(typeof(StringEnumConverter))]
17 | public CVClientCommon.LocationType ImageLocationType { get; set; }
18 |
19 | public InvocationGoogle GoogleInvocation { get; set; } = null;
20 | public InvocationAzure AzureInvocation { get; set; } = null;
21 | public InvocationClarifai ClarifaiInvocation { get; set; } = null;
22 | public InvocationOpenSource OpenSourceInvocation { get; set; } = null;
23 |
24 | public Exception ExceptionRasied { get; private set; } = null;
25 | public DateTime? Completed { get; private set; }
26 |
27 | [JsonIgnore]
28 | public bool HideScoresInFlatResults { get; set; } = false;
29 |
30 | [JsonIgnore]
31 | public Action AllAPIsAction { get { if (action == null) action = GetAction(); return action; } }
32 |
33 | [JsonIgnore]
34 | private Action action = null;
35 |
36 | private Action GetAction()
37 | {
38 | return () =>
39 | {
40 | try
41 | {
42 | var apiTasks = new List();
43 |
44 | if (GoogleInvocation != null)
45 | {
46 | GoogleInvocation.ImageLocation = this.ImageLocation;
47 | GoogleInvocation.ImageLocationType = this.ImageLocationType;
48 | apiTasks.Add(Task.Run(GoogleInvocation.InvocationAction));
49 | }
50 |
51 | if (AzureInvocation != null)
52 | {
53 | AzureInvocation.ImageLocation = this.ImageLocation;
54 | AzureInvocation.ImageLocationType = this.ImageLocationType;
55 | apiTasks.Add(Task.Run(AzureInvocation.InvocationAction));
56 | }
57 |
58 | if (ClarifaiInvocation != null)
59 | {
60 | ClarifaiInvocation.ImageLocation = this.ImageLocation;
61 | ClarifaiInvocation.ImageLocationType = this.ImageLocationType;
62 | apiTasks.Add(Task.Run(ClarifaiInvocation.InvocationAction));
63 | }
64 |
65 | if (OpenSourceInvocation != null)
66 | {
67 | OpenSourceInvocation.ImageLocation = this.ImageLocation;
68 | OpenSourceInvocation.ImageLocationType = this.ImageLocationType;
69 | apiTasks.Add(Task.Run(OpenSourceInvocation.InvocationAction));
70 | }
71 |
72 | Task.WaitAll(apiTasks.ToArray());
73 | }
74 | catch (Exception ex)
75 | {
76 | ExceptionRasied = ex;
77 | }
78 | finally
79 | {
80 | Completed = DateTime.Now;
81 | }
82 | };
83 | }
84 |
85 | [JsonIgnore]
86 | public dynamic FlatResults
87 | {
88 | get
89 | {
90 |
91 | IDictionary flatResults = new ExpandoObject();
92 |
93 | var imageInfoFlatResult = getImageInfoFlat();
94 | mergeObjects(ref flatResults, imageInfoFlatResult, string.Empty);
95 |
96 | if (Completed.HasValue)
97 | {
98 | if (GoogleInvocation != null)
99 | {
100 | mergeObjects(ref flatResults, GoogleInvocation.FlatResult, "GV_");
101 | }
102 |
103 | if (AzureInvocation != null)
104 | {
105 | mergeObjects(ref flatResults, AzureInvocation.FlatResult, "MA_");
106 | }
107 |
108 | if (ClarifaiInvocation != null)
109 | {
110 | mergeObjects(ref flatResults, ClarifaiInvocation.FlatResult, "CL_");
111 | }
112 |
113 | if (OpenSourceInvocation != null)
114 | {
115 | mergeObjects(ref flatResults, OpenSourceInvocation.FlatResult, "OS_");
116 | }
117 | }
118 |
119 | return flatResults;
120 | }
121 | }
122 |
123 | private void mergeObjects(ref IDictionary baseObject, object newObject, string prefixForNewObject)
124 | {
125 | foreach (var property in newObject.GetType().GetProperties())
126 | {
127 | if (property.CanRead)
128 | {
129 | if (HideScoresInFlatResults)
130 | {
131 | if (property.Name.EndsWith("_Score") || property.Name.EndsWith("_Scores"))
132 | continue;
133 | }
134 | baseObject[$"{prefixForNewObject}{property.Name}"] = property.GetValue(newObject);
135 | }
136 | }
137 | }
138 |
139 | private FlatResultImageInfo getImageInfoFlat()
140 | {
141 | var flatResult = new FlatResultImageInfo();
142 |
143 | flatResult.Image_Location = ImageLocation;
144 | flatResult.Image_From = ImageLocationType.ToString();
145 |
146 | if (ImageLocationType == CVClientCommon.LocationType.Local)
147 | {
148 | var fileInfo = new FileInfo(ImageLocation);
149 | flatResult.Image_BaseName = fileInfo.Name;
150 | }
151 | else if (ImageLocationType == CVClientCommon.LocationType.Uri)
152 | {
153 | Uri fileUri = new Uri(ImageLocation);
154 | flatResult.Image_BaseName = fileUri.Segments.Last();
155 | }
156 |
157 | return flatResult;
158 | }
159 |
160 |
161 | }
162 | }
163 |
--------------------------------------------------------------------------------
/CVClient/InvocationOpenSource.cs:
--------------------------------------------------------------------------------
1 | //#define DEBUG_DONOTINVOKEREALAPI
2 |
3 | using System;
4 | using System.Collections.Generic;
5 | using System.IO;
6 | using System.Text;
7 | using System.Linq;
8 | using Newtonsoft.Json;
9 | using System.Net.Http;
10 |
11 | namespace CVClient
12 | {
13 | public class InvocationOpenSource : InvocationBase
14 | {
15 | public InvocationOpenSource()
16 | {
17 | API = CVClientCommon.VisionAPI.OpenSource;
18 | }
19 |
20 | public OpenSourceModelResult APIResponse { get; set; } = null;
21 |
22 | public int DetectionMaxResults { get; set; } = 5;
23 | public double FlatteningMinScore { get; set; } = 0;
24 |
25 | public TimeSpan HTTPConnectionTimeout { get; set; } = new TimeSpan(0, 0, 60);
26 | public TimeSpan HTTPReadTimeout { get; set; } = new TimeSpan(0, 0, 60);
27 |
28 | private string model = null;
29 |
30 | public string Model { get { return model; } set { if (ModelsAvailable.Keys.Contains(value)) model = value; else throw new Exception("This model does not exist"); } }
31 |
32 | private Dictionary modelsAvailable = null;
33 |
34 | [JsonIgnore]
35 | public Dictionary ModelsAvailable
36 | {
37 | get
38 | {
39 | if (modelsAvailable == null)
40 | {
41 | modelsAvailable = CVClientCommon.GetAPIAvailableModels(API.Value);
42 | }
43 |
44 | return modelsAvailable;
45 | }
46 | }
47 |
48 | [JsonIgnore]
49 | public Action InvocationAction { get { if (action == null) action = GetAction(); return action; } }
50 |
51 | [JsonIgnore]
52 | private Action action = null;
53 |
54 | private Action GetAction()
55 | {
56 | return () =>
57 | {
58 | try
59 | {
60 |
61 | #if (!DEBUG_DONOTINVOKEREALAPI)
62 |
63 | string endpoint = Environment.GetEnvironmentVariable("OPEN_SOURCE_CV_ENDPOINT");
64 |
65 | var camelCaseJsonSerializerSetting = new JsonSerializerSettings() { ContractResolver = new Newtonsoft.Json.Serialization.CamelCasePropertyNamesContractResolver() };
66 |
67 | var client = new HttpClient() { Timeout = HTTPConnectionTimeout };
68 | var stringContent = new StringContent(JsonConvert.SerializeObject(GetOpenSourceRequest(), camelCaseJsonSerializerSetting), Encoding.UTF8, "application/json");
69 | var postTask = client.PostAsync(endpoint, stringContent);
70 | System.Diagnostics.Debug.WriteLine($"Posting to API for {this.ImageLocation}");
71 | postTask.Wait(HTTPReadTimeout);
72 | var httpResponse = postTask.Result;
73 | var readStringTask = httpResponse.Content.ReadAsStringAsync();
74 | System.Diagnostics.Debug.WriteLine($"Reading API response for {this.ImageLocation}");
75 | readStringTask.Wait(HTTPReadTimeout);
76 | try
77 | {
78 | APIResponse = JsonConvert.DeserializeObject(readStringTask.Result, camelCaseJsonSerializerSetting);
79 | }
80 | catch
81 | {
82 | throw new Exception($"Invalid response from API: {readStringTask.Result}");
83 | }
84 |
85 | foreach (var modelResult in APIResponse.Results)
86 | {
87 | modelResult.Labels = modelResult.Labels.Where(l => l.Score >= FlatteningMinScore).OrderByDescending(l => l.Score).ToArray();
88 | }
89 | #endif
90 |
91 | #if DEBUG_DONOTINVOKEREALAPI
92 | CVClientCommon.SimulateAPIInvocation();
93 | #endif
94 | }
95 | catch (Exception ex)
96 | {
97 | ExceptionRasied = ex;
98 | }
99 | finally
100 | {
101 |
102 | Processed = DateTime.Now;
103 | }
104 | };
105 | }
106 |
107 | private dynamic GetOpenSourceRequest()
108 | {
109 | if (ImageLocationType == CVClientCommon.LocationType.Uri)
110 | {
111 | return new { Models = new string[] { modelsAvailable[model] }, Image = new { Url = ImageLocation }, TopResults = DetectionMaxResults };
112 | }
113 | else if (ImageLocationType == CVClientCommon.LocationType.Local)
114 | {
115 | return new { Models = new string[] { modelsAvailable[model] }, Image = new { Base64 = Convert.ToBase64String(File.ReadAllBytes(ImageLocation)) }, TopResults = DetectionMaxResults };
116 | }
117 |
118 | return null;
119 | }
120 |
121 | public class OpenSourceModelResult
122 | {
123 | public string Error { get; set; } = null;
124 | public ICollection Results { get; set; }
125 | }
126 |
127 | public class OpenSourceModelResultLabel
128 | {
129 | public string ModelName { get; set; }
130 | public ICollection Labels { get; set; }
131 | }
132 |
133 | public class OpenSourceModelLabel
134 | {
135 | public string Id { get; set; }
136 | public string Label { get; set; }
137 | public double Score { get; set; }
138 | }
139 |
140 | [JsonIgnore]
141 | public FlatResultOpenSource FlatResult
142 | {
143 | get
144 | {
145 | var flatResult = new FlatResultOpenSource();
146 |
147 | if (ExceptionRasied != null)
148 | {
149 | flatResult.Error = ExceptionRasied.Message;
150 | }
151 |
152 | if (APIResponse != null)
153 | {
154 | if (APIResponse.Results.Any())
155 | {
156 | if (string.IsNullOrEmpty(APIResponse.Error))
157 | flatResult.Success = true;
158 | else
159 | flatResult.Error += APIResponse.Error;
160 | }
161 | }
162 |
163 | if (!flatResult.Success)
164 | return flatResult;
165 |
166 | var labels = APIResponse.Results.First().Labels.Where(a => a.Score >= FlatteningMinScore);
167 |
168 | if (labels.Any())
169 | {
170 | flatResult.Labels = string.Join("; ", labels.Select(l => l.Label));
171 | flatResult.Label_Ids = string.Join("; ", labels.Select(l => l.Id));
172 | flatResult.Label_Scores = string.Join("; ", labels.Select(l => l.Score));
173 | }
174 |
175 | flatResult.Model = model;
176 |
177 | return flatResult;
178 | }
179 | }
180 | }
181 | }
182 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 | ##
4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
5 |
6 | # User-specific files
7 | *.rsuser
8 | *.suo
9 | *.user
10 | *.userosscache
11 | *.sln.docstates
12 |
13 | # User-specific files (MonoDevelop/Xamarin Studio)
14 | *.userprefs
15 |
16 | # Build results
17 | [Dd]ebug/
18 | [Dd]ebugPublic/
19 | [Rr]elease/
20 | [Rr]eleases/
21 | x64/
22 | x86/
23 | [Aa][Rr][Mm]/
24 | [Aa][Rr][Mm]64/
25 | bld/
26 | [Bb]in/
27 | [Oo]bj/
28 | [Ll]og/
29 |
30 | # Visual Studio 2015/2017 cache/options directory
31 | .vs/
32 | # Uncomment if you have tasks that create the project's static files in wwwroot
33 | #wwwroot/
34 |
35 | # Visual Studio 2017 auto generated files
36 | Generated\ Files/
37 |
38 | # MSTest test Results
39 | [Tt]est[Rr]esult*/
40 | [Bb]uild[Ll]og.*
41 |
42 | # NUNIT
43 | *.VisualState.xml
44 | TestResult.xml
45 |
46 | # Build Results of an ATL Project
47 | [Dd]ebugPS/
48 | [Rr]eleasePS/
49 | dlldata.c
50 |
51 | # Benchmark Results
52 | BenchmarkDotNet.Artifacts/
53 |
54 | # .NET Core
55 | project.lock.json
56 | project.fragment.lock.json
57 | artifacts/
58 |
59 | # StyleCop
60 | StyleCopReport.xml
61 |
62 | # Files built by Visual Studio
63 | *_i.c
64 | *_p.c
65 | *_h.h
66 | *.ilk
67 | *.meta
68 | *.obj
69 | *.iobj
70 | *.pch
71 | *.pdb
72 | *.ipdb
73 | *.pgc
74 | *.pgd
75 | *.rsp
76 | *.sbr
77 | *.tlb
78 | *.tli
79 | *.tlh
80 | *.tmp
81 | *.tmp_proj
82 | *_wpftmp.csproj
83 | *.log
84 | *.vspscc
85 | *.vssscc
86 | .builds
87 | *.pidb
88 | *.svclog
89 | *.scc
90 |
91 | # Chutzpah Test files
92 | _Chutzpah*
93 |
94 | # Visual C++ cache files
95 | ipch/
96 | *.aps
97 | *.ncb
98 | *.opendb
99 | *.opensdf
100 | *.sdf
101 | *.cachefile
102 | *.VC.db
103 | *.VC.VC.opendb
104 |
105 | # Visual Studio profiler
106 | *.psess
107 | *.vsp
108 | *.vspx
109 | *.sap
110 |
111 | # Visual Studio Trace Files
112 | *.e2e
113 |
114 | # TFS 2012 Local Workspace
115 | $tf/
116 |
117 | # Guidance Automation Toolkit
118 | *.gpState
119 |
120 | # ReSharper is a .NET coding add-in
121 | _ReSharper*/
122 | *.[Rr]e[Ss]harper
123 | *.DotSettings.user
124 |
125 | # JustCode is a .NET coding add-in
126 | .JustCode
127 |
128 | # TeamCity is a build add-in
129 | _TeamCity*
130 |
131 | # DotCover is a Code Coverage Tool
132 | *.dotCover
133 |
134 | # AxoCover is a Code Coverage Tool
135 | .axoCover/*
136 | !.axoCover/settings.json
137 |
138 | # Visual Studio code coverage results
139 | *.coverage
140 | *.coveragexml
141 |
142 | # NCrunch
143 | _NCrunch_*
144 | .*crunch*.local.xml
145 | nCrunchTemp_*
146 |
147 | # MightyMoose
148 | *.mm.*
149 | AutoTest.Net/
150 |
151 | # Web workbench (sass)
152 | .sass-cache/
153 |
154 | # Installshield output folder
155 | [Ee]xpress/
156 |
157 | # DocProject is a documentation generator add-in
158 | DocProject/buildhelp/
159 | DocProject/Help/*.HxT
160 | DocProject/Help/*.HxC
161 | DocProject/Help/*.hhc
162 | DocProject/Help/*.hhk
163 | DocProject/Help/*.hhp
164 | DocProject/Help/Html2
165 | DocProject/Help/html
166 |
167 | # Click-Once directory
168 | publish/
169 |
170 | # Publish Web Output
171 | *.[Pp]ublish.xml
172 | *.azurePubxml
173 | # Note: Comment the next line if you want to checkin your web deploy settings,
174 | # but database connection strings (with potential passwords) will be unencrypted
175 | *.pubxml
176 | *.publishproj
177 |
178 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
179 | # checkin your Azure Web App publish settings, but sensitive information contained
180 | # in these scripts will be unencrypted
181 | PublishScripts/
182 |
183 | # NuGet Packages
184 | *.nupkg
185 | # The packages folder can be ignored because of Package Restore
186 | **/[Pp]ackages/*
187 | # except build/, which is used as an MSBuild target.
188 | !**/[Pp]ackages/build/
189 | # Uncomment if necessary however generally it will be regenerated when needed
190 | #!**/[Pp]ackages/repositories.config
191 | # NuGet v3's project.json files produces more ignorable files
192 | *.nuget.props
193 | *.nuget.targets
194 |
195 | # Microsoft Azure Build Output
196 | csx/
197 | *.build.csdef
198 |
199 | # Microsoft Azure Emulator
200 | ecf/
201 | rcf/
202 |
203 | # Windows Store app package directories and files
204 | AppPackages/
205 | BundleArtifacts/
206 | Package.StoreAssociation.xml
207 | _pkginfo.txt
208 | *.appx
209 |
210 | # Visual Studio cache files
211 | # files ending in .cache can be ignored
212 | *.[Cc]ache
213 | # but keep track of directories ending in .cache
214 | !?*.[Cc]ache/
215 |
216 | # Others
217 | ClientBin/
218 | ~$*
219 | *~
220 | *.dbmdl
221 | *.dbproj.schemaview
222 | *.jfm
223 | *.pfx
224 | *.publishsettings
225 | orleans.codegen.cs
226 |
227 | # Including strong name files can present a security risk
228 | # (https://github.com/github/gitignore/pull/2483#issue-259490424)
229 | #*.snk
230 |
231 | # Since there are multiple workflows, uncomment next line to ignore bower_components
232 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
233 | #bower_components/
234 |
235 | # RIA/Silverlight projects
236 | Generated_Code/
237 |
238 | # Backup & report files from converting an old project file
239 | # to a newer Visual Studio version. Backup files are not needed,
240 | # because we have git ;-)
241 | _UpgradeReport_Files/
242 | Backup*/
243 | UpgradeLog*.XML
244 | UpgradeLog*.htm
245 | ServiceFabricBackup/
246 | *.rptproj.bak
247 |
248 | # SQL Server files
249 | *.mdf
250 | *.ldf
251 | *.ndf
252 |
253 | # Business Intelligence projects
254 | *.rdl.data
255 | *.bim.layout
256 | *.bim_*.settings
257 | *.rptproj.rsuser
258 | *- Backup*.rdl
259 |
260 | # Microsoft Fakes
261 | FakesAssemblies/
262 |
263 | # GhostDoc plugin setting file
264 | *.GhostDoc.xml
265 |
266 | # Node.js Tools for Visual Studio
267 | .ntvs_analysis.dat
268 | node_modules/
269 |
270 | # Visual Studio 6 build log
271 | *.plg
272 |
273 | # Visual Studio 6 workspace options file
274 | *.opt
275 |
276 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
277 | *.vbw
278 |
279 | # Visual Studio LightSwitch build output
280 | **/*.HTMLClient/GeneratedArtifacts
281 | **/*.DesktopClient/GeneratedArtifacts
282 | **/*.DesktopClient/ModelManifest.xml
283 | **/*.Server/GeneratedArtifacts
284 | **/*.Server/ModelManifest.xml
285 | _Pvt_Extensions
286 |
287 | # Paket dependency manager
288 | .paket/paket.exe
289 | paket-files/
290 |
291 | # FAKE - F# Make
292 | .fake/
293 |
294 | # JetBrains Rider
295 | .idea/
296 | *.sln.iml
297 |
298 | # CodeRush personal settings
299 | .cr/personal
300 |
301 | # Python Tools for Visual Studio (PTVS)
302 | __pycache__/
303 | *.pyc
304 |
305 | # Cake - Uncomment if you are using it
306 | # tools/**
307 | # !tools/packages.config
308 |
309 | # Tabs Studio
310 | *.tss
311 |
312 | # Telerik's JustMock configuration file
313 | *.jmconfig
314 |
315 | # BizTalk build output
316 | *.btp.cs
317 | *.btm.cs
318 | *.odx.cs
319 | *.xsd.cs
320 |
321 | # OpenCover UI analysis results
322 | OpenCover/
323 |
324 | # Azure Stream Analytics local run output
325 | ASALocalRun/
326 |
327 | # MSBuild Binary and Structured Log
328 | *.binlog
329 |
330 | # NVidia Nsight GPU debugger configuration file
331 | *.nvuser
332 |
333 | # MFractors (Xamarin productivity tool) working folder
334 | .mfractor/
335 |
336 | # Local History for Visual Studio
337 | .localhistory/
338 |
339 | # BeatPulse healthcheck temp database
340 | healthchecksdb
--------------------------------------------------------------------------------
/Memespector-GUI/Utilities.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Diagnostics;
4 | using System.Linq;
5 | using System.Runtime.InteropServices;
6 | using System.Text;
7 | using System.IO;
8 | using Newtonsoft.Json;
9 | using System.Threading.Tasks;
10 | using Avalonia.Controls;
11 | using Google.Cloud.Vision.V1;
12 | using MessageBox.Avalonia;
13 | using Microsoft.Azure.CognitiveServices.Vision.ComputerVision.Models;
14 |
15 | namespace Memespector_GUI
16 | {
17 | static public class Utilities
18 | {
19 | static private MemespectorConfig? memespectorConfig = null;
20 | static public MemespectorConfig MemespectorConfig
21 | {
22 | get
23 | {
24 | if (memespectorConfig == null)
25 | {
26 | try
27 | {
28 | var configFilePath = Path.Join(GetApplicationPath(), configBaseFilename);
29 | memespectorConfig = JsonConvert.DeserializeObject(File.ReadAllText(configFilePath));
30 | }
31 | catch
32 | {
33 | memespectorConfig = new MemespectorConfig();
34 | WriteConfig(memespectorConfig);
35 | }
36 | }
37 | return memespectorConfig;
38 | }
39 | }
40 |
41 | static public Dictionary GetConfigMicrosoftAzureFlatteningMinScores()
42 | {
43 | var config = MemespectorConfig;
44 | return new Dictionary {
45 | { VisualFeatureTypes.Categories, config.MicrosoftAzure_CSV_MinScores.Categories },
46 | { VisualFeatureTypes.Description, config.MicrosoftAzure_CSV_MinScores.Description },
47 | { VisualFeatureTypes.Tags, config.MicrosoftAzure_CSV_MinScores.Tags },
48 | { VisualFeatureTypes.Brands, config.MicrosoftAzure_CSV_MinScores.Brands },
49 | { VisualFeatureTypes.Objects, config.MicrosoftAzure_CSV_MinScores.Objects }
50 | };
51 | }
52 |
53 | static public Dictionary GetConfigGoogleVisionMaxResults()
54 | {
55 | var config = MemespectorConfig;
56 | return new Dictionary {
57 | { Feature.Types.Type.FaceDetection, config.GoogleVision_MaxResults.Face },
58 | { Feature.Types.Type.LabelDetection, config.GoogleVision_MaxResults.Label },
59 | { Feature.Types.Type.WebDetection, config.GoogleVision_MaxResults.Web },
60 | { Feature.Types.Type.LandmarkDetection, config.GoogleVision_MaxResults.Landmark },
61 | { Feature.Types.Type.LogoDetection, config.GoogleVision_MaxResults.Logo }
62 | };
63 | }
64 |
65 | static public Dictionary GetConfigGoogleVisionFlatteningMinScores()
66 | {
67 | var config = MemespectorConfig;
68 | return new Dictionary {
69 | { Feature.Types.Type.LabelDetection, config.GoogleVision_CSV_MinScores.Label },
70 | { Feature.Types.Type.WebDetection, config.GoogleVision_CSV_MinScores.Web },
71 | { Feature.Types.Type.LandmarkDetection, config.GoogleVision_CSV_MinScores.Landmark },
72 | { Feature.Types.Type.LogoDetection, config.GoogleVision_CSV_MinScores.Logo },
73 | { Feature.Types.Type.FaceDetection, config.GoogleVision_CSV_MinScores.Face }
74 | };
75 | }
76 |
77 | static public void WriteConfig(MemespectorConfig config)
78 | {
79 | var configFilePath = Path.Join(GetApplicationPath(), configBaseFilename);
80 | File.WriteAllText(configFilePath, JsonConvert.SerializeObject(config, Formatting.Indented));
81 | memespectorConfig = config;
82 | }
83 |
84 | static public bool IsFolderPath(string filename)
85 | {
86 | return Directory.Exists(filename);
87 | }
88 |
89 | static public bool IsFilePath(string filename)
90 | {
91 | return File.Exists(filename);
92 | }
93 |
94 | static public bool IsUrl(string url)
95 | {
96 | return Uri.TryCreate(url, UriKind.Absolute, out _);
97 | }
98 |
99 | static public bool OpenExternalUrl(string url)
100 | {
101 | if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
102 | {
103 | Process.Start(new ProcessStartInfo("cmd", $"/c start {url.Replace("&", "^&")}") { CreateNoWindow = true });
104 | return true;
105 | }
106 | if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
107 | {
108 | Process.Start("xdg-open", url);
109 | return true;
110 | }
111 | if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
112 | {
113 | Process.Start("open", url);
114 | return true;
115 | }
116 | return false;
117 | }
118 |
119 |
120 | static public async void ShowErrorDialog(string title, string message, Window parent)
121 | {
122 | var messageBox = MessageBoxManager.GetMessageBoxStandardWindow(new MessageBox.Avalonia.DTO.MessageBoxStandardParams
123 | {
124 | ButtonDefinitions = MessageBox.Avalonia.Enums.ButtonEnum.Ok,
125 | ContentTitle = title,
126 | ContentMessage = message,
127 | Icon = MessageBox.Avalonia.Enums.Icon.Error,
128 | WindowStartupLocation = WindowStartupLocation.CenterScreen,
129 | Style = MessageBox.Avalonia.Enums.Style.Windows
130 | });
131 | await messageBox.ShowDialog(parent);
132 | }
133 |
134 | static public async void ShowInfoDialog(string title, string message, Window parent)
135 | {
136 | var messageBox = MessageBoxManager.GetMessageBoxStandardWindow(new MessageBox.Avalonia.DTO.MessageBoxStandardParams
137 | {
138 | ButtonDefinitions = MessageBox.Avalonia.Enums.ButtonEnum.Ok,
139 | ContentTitle = title,
140 | ContentMessage = message,
141 | Icon = MessageBox.Avalonia.Enums.Icon.Info,
142 | WindowStartupLocation = WindowStartupLocation.CenterScreen,
143 | Style = MessageBox.Avalonia.Enums.Style.Windows
144 | });
145 | await messageBox.ShowDialog(parent);
146 | }
147 |
148 | static public async Task ShowOkCancelDialog(string title, string message, Window parent)
149 | {
150 | var messageBox = MessageBoxManager.GetMessageBoxStandardWindow(new MessageBox.Avalonia.DTO.MessageBoxStandardParams
151 | {
152 | ButtonDefinitions = MessageBox.Avalonia.Enums.ButtonEnum.OkCancel,
153 | ContentTitle = title,
154 | ContentMessage = message,
155 | Icon = MessageBox.Avalonia.Enums.Icon.Warning,
156 | WindowStartupLocation = WindowStartupLocation.CenterScreen,
157 | Style = MessageBox.Avalonia.Enums.Style.Windows
158 | });
159 | return await messageBox.ShowDialog(parent);
160 | }
161 |
162 | static public string GetApplicationPath()
163 | {
164 | #pragma warning disable CS8603 // Possible null reference return.
165 | #pragma warning disable CS8602 // Dereference of a possibly null reference.
166 | return System.IO.Path.GetDirectoryName(Process.GetCurrentProcess().MainModule.FileName);
167 | #pragma warning restore CS8602 // Dereference of a possibly null reference.
168 | #pragma warning restore CS8603 // Possible null reference return.
169 | }
170 |
171 | static private string configBaseFilename = "config-memespector-gui.json";
172 | }
173 | }
174 |
--------------------------------------------------------------------------------
/CVClient/InvocationAzure.cs:
--------------------------------------------------------------------------------
1 | //#define DEBUG_DONOTINVOKEREALAPI
2 |
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Text;
6 | using System.Linq;
7 | using Microsoft.Azure.CognitiveServices.Vision.ComputerVision;
8 | using Microsoft.Azure.CognitiveServices.Vision.ComputerVision.Models;
9 | using Newtonsoft.Json;
10 | using System.Threading.Tasks;
11 | using System.IO;
12 | using Newtonsoft.Json.Converters;
13 |
14 | namespace CVClient
15 | {
16 | public class InvocationAzure : InvocationBase
17 | {
18 | public InvocationAzure ()
19 | {
20 | API = CVClientCommon.VisionAPI.MicrosoftAzure;
21 |
22 | }
23 |
24 | public ImageAnalysis APIResponse { get; set; } = null;
25 |
26 | public ICollection DetectionFeatureTypes { get; set; } = new List();
27 |
28 | public Dictionary FlatteningMinScores { get; set; } = new Dictionary {
29 | { VisualFeatureTypes.Categories, 0 },
30 | { VisualFeatureTypes.Description, 0 },
31 | { VisualFeatureTypes.Tags, 0 },
32 | { VisualFeatureTypes.Brands, 0 },
33 | { VisualFeatureTypes.Objects, 0 }
34 | };
35 |
36 | [JsonIgnore]
37 | public Action InvocationAction { get { if (action == null) action = GetAction(); return action; } }
38 |
39 | [JsonIgnore]
40 | private Action action = null;
41 |
42 | private Action GetAction()
43 | {
44 | return () =>
45 | {
46 | try
47 | {
48 |
49 | #if (!DEBUG_DONOTINVOKEREALAPI)
50 |
51 | var client = new ComputerVisionClient(new ApiKeyServiceClientCredentials(Environment.GetEnvironmentVariable("MICROSOFT_AZURE_CV_KEY"))) { Endpoint = Environment.GetEnvironmentVariable("MICROSOFT_AZURE_CV_ENDPOINT") };
52 | Task imageAnalysisTask = null;
53 |
54 | if (ImageLocationType == CVClientCommon.LocationType.Local)
55 | {
56 | Stream imageStream = new MemoryStream(File.ReadAllBytes(ImageLocation));
57 | imageAnalysisTask = client.AnalyzeImageInStreamAsync(imageStream, visualFeatures: DetectionFeatureTypes.ToList());
58 | }
59 | else if (ImageLocationType == CVClientCommon.LocationType.Uri)
60 | {
61 | imageAnalysisTask = client.AnalyzeImageAsync(ImageLocation, visualFeatures: DetectionFeatureTypes.ToList());
62 | }
63 |
64 | imageAnalysisTask.Wait();
65 | APIResponse = imageAnalysisTask.Result;
66 | #endif
67 |
68 | #if DEBUG_DONOTINVOKEREALAPI
69 | CVClientCommon.SimulateAPIInvocation();
70 | #endif
71 | }
72 | catch (Exception ex)
73 | {
74 | ExceptionRasied = ex;
75 | }
76 | finally
77 | {
78 |
79 | Processed = DateTime.Now;
80 | }
81 | };
82 | }
83 |
84 | [JsonIgnore]
85 | public FlatResultAzure FlatResult
86 | {
87 | get
88 | {
89 | var flatResult = new FlatResultAzure();
90 |
91 | if (ExceptionRasied != null)
92 | {
93 | flatResult.Error = ExceptionRasied.Message;
94 | }
95 |
96 | if (APIResponse != null)
97 | {
98 | flatResult.Success = true;
99 | }
100 |
101 | if (!flatResult.Success)
102 | return flatResult;
103 |
104 | foreach (var featureType in DetectionFeatureTypes)
105 | {
106 | switch (featureType)
107 | {
108 | case VisualFeatureTypes.Categories:
109 | var categories = APIResponse.Categories.Where(a => a.Score >= FlatteningMinScores[VisualFeatureTypes.Categories]);
110 | if (categories.Any())
111 | {
112 | flatResult.Categories = string.Join("; ", categories.Select(l => l.Name));
113 | flatResult.Categories_Scores = string.Join("; ", categories.Select(l => l.Score));
114 | }
115 | break;
116 | case VisualFeatureTypes.Adult:
117 | if (APIResponse.Adult != null)
118 | {
119 | flatResult.Adult_Adult = APIResponse.Adult.IsAdultContent;
120 | flatResult.Adult_Racy = APIResponse.Adult.IsRacyContent;
121 | flatResult.Adult_Gory = APIResponse.Adult.IsGoryContent;
122 |
123 | flatResult.Adult_Adult_Score = APIResponse.Adult.AdultScore.ToString();
124 | flatResult.Adult_Racy_Score = APIResponse.Adult.RacyScore.ToString();
125 | flatResult.Adult_Gory_Score = APIResponse.Adult.GoreScore.ToString();
126 | }
127 | break;
128 | case VisualFeatureTypes.Tags:
129 | var tags = APIResponse.Tags.Where(a => a.Confidence >= FlatteningMinScores[VisualFeatureTypes.Tags]);
130 | if (tags.Any())
131 | {
132 | flatResult.Tags = string.Join("; ", tags.Select(l => l.Name));
133 | flatResult.Tag_Scores = string.Join("; ", tags.Select(l => l.Confidence));
134 | }
135 | break;
136 | case VisualFeatureTypes.Description:
137 | if (APIResponse.Description.Tags.Any())
138 | {
139 | flatResult.Description_Tags = string.Join("; ", APIResponse.Description.Tags);
140 | }
141 | var captions = APIResponse.Description.Captions.Where(a => a.Confidence >= FlatteningMinScores[VisualFeatureTypes.Description]);
142 | if (captions.Any())
143 | {
144 | flatResult.Description_Captions = string.Join("; ", captions.Select(l => l.Text));
145 | flatResult.Description_Caption_Scores = string.Join("; ", captions.Select(l => l.Confidence));
146 | }
147 | break;
148 | case VisualFeatureTypes.Faces:
149 | if (APIResponse.Faces.Any())
150 | {
151 | flatResult.Face_Ages = string.Join("; ", APIResponse.Faces.Select(l => l.Age));
152 | flatResult.Face_Genders = string.Join("; ", APIResponse.Faces.Select(l => l.Gender));
153 | }
154 | break;
155 | case VisualFeatureTypes.Objects:
156 | var objects = APIResponse.Objects.Where(a => a.Confidence >= FlatteningMinScores[VisualFeatureTypes.Objects]);
157 | if (objects.Any())
158 | {
159 | flatResult.Objects = string.Join("; ", objects.Select(l => l.ObjectProperty));
160 | flatResult.Object_Scores = string.Join("; ", objects.Select(l => l.Confidence));
161 | }
162 | break;
163 | case VisualFeatureTypes.Brands:
164 | var brands = APIResponse.Brands.Where(a => a.Confidence >= FlatteningMinScores[VisualFeatureTypes.Brands]);
165 | if (brands.Any())
166 | {
167 | flatResult.Brands = string.Join("; ", brands.Select(l => l.Name));
168 | flatResult.Brand_Scores = string.Join("; ", brands.Select(l => l.Confidence));
169 | }
170 | break;
171 | default:
172 | break;
173 | }
174 | }
175 |
176 | return flatResult;
177 | }
178 | }
179 | }
180 | }
181 |
--------------------------------------------------------------------------------
/CVClient/InvocationGoogle.cs:
--------------------------------------------------------------------------------
1 | // #define DEBUG_DONOTINVOKEREALAPI
2 |
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Linq;
6 | using Google.Cloud.Vision.V1;
7 | using Newtonsoft.Json;
8 |
9 | namespace CVClient
10 | {
11 | public class InvocationGoogle : InvocationBase
12 | {
13 | public InvocationGoogle()
14 | {
15 | API = CVClientCommon.VisionAPI.GoogleCloud;
16 | }
17 |
18 | public AnnotateImageResponse APIResponse { get; set; } = null;
19 |
20 | public ICollection DetectionFeatureTypes { get; set; } = new List();
21 | public Dictionary DetectionMaxResults { get; set; } = new Dictionary {
22 | { Feature.Types.Type.SafeSearchDetection, 0 },
23 | { Feature.Types.Type.FaceDetection, 0 },
24 | { Feature.Types.Type.LabelDetection, 0 },
25 | { Feature.Types.Type.WebDetection, 0 },
26 | { Feature.Types.Type.TextDetection, 0 },
27 | { Feature.Types.Type.LandmarkDetection, 0 },
28 | { Feature.Types.Type.LogoDetection, 0 }
29 | };
30 | public Dictionary FlatteningMinScores { get; set; } = new Dictionary {
31 | { Feature.Types.Type.SafeSearchDetection, 0 },
32 | { Feature.Types.Type.FaceDetection, 0 },
33 | { Feature.Types.Type.LabelDetection, 0 },
34 | { Feature.Types.Type.WebDetection, 0 },
35 | { Feature.Types.Type.TextDetection, 0 },
36 | { Feature.Types.Type.LandmarkDetection, 0 },
37 | { Feature.Types.Type.LogoDetection, 0 }
38 | };
39 |
40 | [JsonIgnore]
41 | public Action InvocationAction { get { if (action == null) action = GetAction(); return action; } }
42 |
43 | [JsonIgnore]
44 | private Action action = null;
45 |
46 | private Action GetAction()
47 | {
48 | return () =>
49 | {
50 | try
51 | {
52 | #if (!DEBUG_DONOTINVOKEREALAPI)
53 | var iaClient = ImageAnnotatorClient.Create();
54 | APIResponse = iaClient.Annotate(GetAnnotateImageRequest());
55 | #endif
56 |
57 | #if DEBUG_DONOTINVOKEREALAPI
58 | CVClientCommon.SimulateAPIInvocation();
59 | #endif
60 | }
61 | catch (Exception ex)
62 | {
63 | ExceptionRasied = ex;
64 | }
65 | finally
66 | {
67 |
68 | Processed = DateTime.Now;
69 | }
70 | };
71 | }
72 |
73 | public AnnotateImageRequest GetAnnotateImageRequest()
74 | {
75 | var request = new AnnotateImageRequest();
76 |
77 | if (ImageLocationType == CVClientCommon.LocationType.Local)
78 | {
79 | request.Image = Image.FromFile(ImageLocation);
80 | }
81 | else if (ImageLocationType == CVClientCommon.LocationType.Uri)
82 | {
83 | request.Image = Image.FromUri(ImageLocation);
84 | }
85 |
86 | foreach (var feature in DetectionFeatureTypes)
87 | {
88 | var newFeature = new Feature() { Type = feature };
89 |
90 | if (DetectionMaxResults.Keys.Contains(feature))
91 | {
92 | if (DetectionMaxResults[feature] > 0)
93 | newFeature.MaxResults = DetectionMaxResults[feature];
94 | }
95 |
96 | request.Features.Add(newFeature);
97 | }
98 |
99 | return request;
100 | }
101 |
102 |
103 | [JsonIgnore]
104 | public FlatResultGoogle FlatResult
105 | {
106 | get
107 | {
108 | var flatResult = new FlatResultGoogle();
109 |
110 | if (ExceptionRasied != null)
111 | {
112 | flatResult.Error = ExceptionRasied.Message;
113 | }
114 |
115 | if (APIResponse != null)
116 | {
117 | flatResult.Success = true;
118 | }
119 |
120 | if (!flatResult.Success)
121 | return flatResult;
122 |
123 | foreach (var featureType in DetectionFeatureTypes)
124 | {
125 | switch (featureType)
126 | {
127 | case Feature.Types.Type.FaceDetection:
128 | var faceAnnotations = APIResponse.FaceAnnotations.Where(a => a.DetectionConfidence >= FlatteningMinScores[Feature.Types.Type.FaceDetection]);
129 | if (faceAnnotations.Any())
130 | {
131 | flatResult.Face_Joy = string.Join("; ", faceAnnotations.Select(l => l.JoyLikelihood.ToString()));
132 | flatResult.Face_Sorrow = string.Join("; ", faceAnnotations.Select(l => l.SorrowLikelihood.ToString()));
133 | flatResult.Face_Anger = string.Join("; ", faceAnnotations.Select(l => l.AngerLikelihood.ToString()));
134 | flatResult.Face_Surprise = string.Join("; ", faceAnnotations.Select(l => l.SurpriseLikelihood.ToString()));
135 | flatResult.Face_UnderExposed = string.Join("; ", faceAnnotations.Select(l => l.UnderExposedLikelihood.ToString()));
136 | flatResult.Face_Blurred = string.Join("; ", faceAnnotations.Select(l => l.BlurredLikelihood.ToString()));
137 | flatResult.Face_Headwear = string.Join("; ", faceAnnotations.Select(l => l.HeadwearLikelihood.ToString()));
138 |
139 | flatResult.Face_Score = string.Join("; ", faceAnnotations.Select(l => l.DetectionConfidence.ToString()));
140 | }
141 | break;
142 | case Feature.Types.Type.LandmarkDetection:
143 | var landmarkAnnotations = APIResponse.LandmarkAnnotations.Where(a => a.Score >= FlatteningMinScores[Feature.Types.Type.LandmarkDetection]);
144 | if (landmarkAnnotations.Any())
145 | {
146 | flatResult.Landmark_Ids = string.Join("; ", landmarkAnnotations.Select(l => l.Mid));
147 | flatResult.Landmark_Descriptions = string.Join("; ", landmarkAnnotations.Select(l => l.Description));
148 | flatResult.Landmark_Scores = string.Join("; ", landmarkAnnotations.Select(l => l.Score));
149 | }
150 | break;
151 | case Feature.Types.Type.LogoDetection:
152 | var logoAnnotations = APIResponse.LogoAnnotations.Where(a => a.Score >= FlatteningMinScores[Feature.Types.Type.LogoDetection]);
153 | if (logoAnnotations.Any())
154 | {
155 | flatResult.Logo_Ids = string.Join("; ", logoAnnotations.Select(l => l.Mid));
156 | flatResult.Logo_Descriptions = string.Join("; ", logoAnnotations.Select(l => l.Description));
157 | flatResult.Logo_Scores = string.Join("; ", logoAnnotations.Select(l => l.Score));
158 | }
159 | break;
160 | case Feature.Types.Type.LabelDetection:
161 | var labelAnnotations = APIResponse.LabelAnnotations.Where(a => a.Score >= FlatteningMinScores[Feature.Types.Type.LabelDetection]);
162 | if (labelAnnotations.Any())
163 | {
164 | flatResult.Label_Ids = string.Join("; ", labelAnnotations.Select(l => l.Mid));
165 | flatResult.Label_Descriptions = string.Join("; ", labelAnnotations.Select(l => l.Description));
166 | flatResult.Label_Scores = string.Join("; ", labelAnnotations.Select(l => l.Score));
167 | }
168 | break;
169 | case Feature.Types.Type.TextDetection:
170 | if (APIResponse.FullTextAnnotation != null)
171 | {
172 | flatResult.Text = APIResponse.FullTextAnnotation.Text;
173 | }
174 | break;
175 | case Feature.Types.Type.SafeSearchDetection:
176 | if (APIResponse.SafeSearchAnnotation != null)
177 | {
178 | flatResult.Safe_Adult = APIResponse.SafeSearchAnnotation.Adult.ToString();
179 | flatResult.Safe_Spoof = APIResponse.SafeSearchAnnotation.Spoof.ToString();
180 | flatResult.Safe_Medical = APIResponse.SafeSearchAnnotation.Medical.ToString();
181 | flatResult.Safe_Violence = APIResponse.SafeSearchAnnotation.Violence.ToString();
182 | flatResult.Safe_Racy = APIResponse.SafeSearchAnnotation.Racy.ToString();
183 | }
184 | break;
185 | case Feature.Types.Type.WebDetection:
186 | if (APIResponse.WebDetection != null)
187 | {
188 | float webMinScore = FlatteningMinScores[Feature.Types.Type.WebDetection];
189 | var webEntities = APIResponse.WebDetection.WebEntities.Where(a => a.Score >= webMinScore);
190 | flatResult.Web_Entity_Ids = string.Join("; ", webEntities.Select(l => l.EntityId));
191 | flatResult.Web_Entity_Descriptions = string.Join("; ", webEntities.Select(l => l.Description));
192 | flatResult.Web_Entity_Scores = string.Join("; ", webEntities.Select(l => l.Score));
193 |
194 | flatResult.Web_FullMatchingImages = string.Join(" ; ", APIResponse.WebDetection.FullMatchingImages.Select(i => i.Url));
195 | flatResult.Web_PartialMatchingImages = string.Join(" ; ", APIResponse.WebDetection.PartialMatchingImages.Select(i => i.Url));
196 | flatResult.Web_PagesWithFullMatchingImages = string.Join(" ; ", APIResponse.WebDetection.PagesWithMatchingImages.Select(i => i.Url));
197 | flatResult.Web_VisuallySimilarImages = string.Join(" ; ", APIResponse.WebDetection.VisuallySimilarImages.Select(i => i.Url));
198 | flatResult.Web_BestGuessLabels = string.Join("; ", APIResponse.WebDetection.BestGuessLabels.Select(l => l.Label));
199 | }
200 | break;
201 | default:
202 | break;
203 | }
204 | }
205 |
206 | return flatResult;
207 | }
208 | }
209 | }
210 | }
211 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Memespector-GUI [](https://zenodo.org/badge/latestdoi/333639175)
2 |
3 | Memespector-GUI is a cross-platform client for computer vision APIs with a graphical user interface. Memespector-GUI supports [Google Cloud Vision API](https://cloud.google.com/vision/), [Microsoft Azure Cognitive Services](https://azure.microsoft.com/en-us/services/cognitive-services/computer-vision/), [Clarifai Computer Vision](https://www.clarifai.com/) and an API for [Keras' built-in open source models](https://keras.io/api/applications/).
4 |
5 | Memespector-GUI runs on Windows, Mac OS and Linux.
6 |
7 | 
8 |
9 | If you use this tool in your research, please cite it.
10 | ### APA
11 | ```
12 | Chao, J. (2023). Memespector-GUI: Graphical User Interface Client for Computer Vision APIs (Version 0.2.5) [Computer software]. https://doi.org/10.5281/zenodo.7704877
13 | ```
14 | ### BibTeX
15 | ```
16 | @software{Chao_Memespector-GUI_Graphical_User_2023,
17 | author = {Chao, Jason},
18 | doi = {10.5281/zenodo.7704877},
19 | month = {3},
20 | title = {{Memespector-GUI: Graphical User Interface Client for Computer Vision APIs}},
21 | url = {https://github.com/jason-chao/memespector-gui},
22 | version = {0.2.5},
23 | year = {2023}
24 | }
25 | ```
26 |
27 | # Contents
28 | * [Download](#download)
29 | * [Gaining access to APIs](#gaining-access-to-apis)
30 | * Get authentication keys from
31 | * [Google Vision](doc/GetKeyFromGoogleCloud.md)
32 | * [Microsoft Azure Cognitive Services](doc/GetKeyFromMicrosoftAzure.md)
33 | * [Clarifai](doc/GetKeyFromClarifai.md)
34 | * [Usage](#usage)
35 | * [Change API settings](#change-api-settings)
36 | * [Google Vision](#google-vision)
37 | * [Microsoft Azure Cognitive Services](#microsoft-azure-cognitive-services)
38 | * [Clarifai](#Clarifai)
39 | * [Open source models](#open-source-models)
40 | * [Add images to Memespector-GUI](#add-images-to-memespector-gui)
41 | * [Images on a computer](#images-on-a-computer)
42 | * [Images on the web](#images-on-the-web)
43 | * [Edit the names for output files](#edit-the-names-for-output-files)
44 | * [Invoke the APIs](#invoke-the-apis)
45 | * [Credits](#credits)
46 |
47 | # Download
48 |
49 | Downlaod [the latest version of Memespector-GUI for Windows, Mac OS or Linux](https://github.com/jason-chao/memespector-gui/releases/).
50 |
51 | When you open Memespector-GUI for the first time on Windows or Mac, the operating system may decline to run Memespector-GUI because of tightened security measures. If this happens, please follow [the instructions on how to allow Memespector-GUI to run at first launch on Windows and Mac](doc/FirstRun.md).
52 |
53 | # Gaining access to APIs
54 |
55 | You have to get authentication keys from Google Cloud, Microsoft Azure and Clarifai in order to use their APIs.
56 |
57 | 1. If you wish to use **Google Vision API**, you must obtain a credential key file from Google Cloud. See [the instructions on how to activate Google Cloud Service and obtain an authentication file](doc/GetKeyFromGoogleCloud.md).
58 | 2. If you wish to use **Microsoft Azure Cognitive Services Computer Vision**, you must obtain a service endpoint and subscription key from Microsoft Azure. See [the instructions on how to activate Microsoft Azure and obtain authentication keys](doc/GetKeyFromMicrosoftAzure.md).
59 | 3. If you wish to use **Clarifai**, you must obtain an API key from Clarifai. See [the instructions on how to obtain an API key from Clarifai](doc/GetKeyFromClarifai.md).
60 |
61 | # Usage
62 |
63 | *Note: The graphical user interfaces of Memespsector-GUI are nearly identical across the Windows, Mac and Linux versions. For illustrative purpose, the screenshots of the Windows version are used in this document. The following steps also apply to the Mac and Linux versions of Memespector-GUI.*
64 |
65 | ## Change API settings
66 |
67 | 
68 |
69 | ### Google Vision
70 |
71 | #### Select the credential key file
72 |
73 | Check the box for `Google Vision`. Click `Browse` to the right of the `Credential file` box.
74 |
75 | 
76 |
77 | Select the .json file you downloaded from Google Cloud. See [the detailed instructions on how to obtain a credential file from Google Cloud](doc/GetKeyFromGoogleCloud.md#obtaining-a-credential-key-file).
78 |
79 | 
80 |
81 | #### Select the features to be detected
82 |
83 | Check the features that you want Google Vision API to detect in the images.
84 |
85 | 
86 |
87 | - [Safety](https://cloud.google.com/vision/docs/detecting-safe-search) - adult, violent and racist elements
88 | - [Face](https://cloud.google.com/vision/docs/detecting-faces) - emotional expressions of faces
89 | - [Label](https://cloud.google.com/vision/docs/labels) - generalised labels defined by Google
90 | - [Web](https://cloud.google.com/vision/docs/detecting-web) - web entities (inferred descriptions from similar images on the web), similar images, full/partial matching images, visually similar images and web pages with matching images
91 | - [Text](https://cloud.google.com/vision/docs/ocr) - the text recognised
92 | - [Landmark](https://cloud.google.com/vision/docs/detecting-landmarks) - well-known or prominent sites
93 | - [Logo](https://cloud.google.com/vision/docs/detecting-logos) - logos of popular products
94 |
95 | ### Microsoft Azure Cognitive Services
96 |
97 | #### Paste the Endpoint and Subscription key
98 |
99 | Check the box for `Microsoft Azure`. Paste the Endpoint and Subscription key you obtained from Microsoft Azure into the `Endpoint` and `Subscription key` boxes. See [the detailed instructions on how to obtain an endpoint and a key from Microsoft Azure](doc/GetKeyFromMicrosoftAzure.md#obtaining-a-service-endpoint-and-a-subscription-key).
100 |
101 | 
102 |
103 | #### Select the features to be detected
104 |
105 | Check the features that you want Microsoft Azure Cognitive Services to detect in the images.
106 |
107 | 
108 |
109 | - [Adult](https://docs.microsoft.com/en-us/azure/cognitive-services/computer-vision/concept-detecting-adult-content) - explicitly sexual, sexually suggestive and blood/gore
110 | - [Brands](https://docs.microsoft.com/en-us/azure/cognitive-services/computer-vision/concept-brand-detection) - logos of brands in consumer electronics, clothing and more
111 | - [Categories](https://docs.microsoft.com/en-us/azure/cognitive-services/computer-vision/category-taxonomy) - 86-category taxonomy
112 | - [Description](https://docs.microsoft.com/en-us/azure/cognitive-services/computer-vision/concept-describing-images) - a human-readable sentence that describes the image's contents
113 | - [Face](https://docs.microsoft.com/en-us/azure/cognitive-services/computer-vision/concept-detecting-faces) - human faces with age and gender
114 | - [Objects](https://docs.microsoft.com/en-us/azure/cognitive-services/computer-vision/concept-object-detection) - objects or living things with bounding box coordinates
115 | - [Tags](https://docs.microsoft.com/en-us/azure/cognitive-services/computer-vision/concept-tagging-images) - recognisable objects, living beings, scenery and actions
116 |
117 | ### Clarifai
118 |
119 | Check the box for `Clarifai`. Paste the API key you obtained from Clarifai into the `API key` box. See [the detailed instructions on how to obtain an API key from Clarifai](doc/GetKeyFromClarifai.md#obtaining-an-api-key).
120 |
121 | 
122 |
123 | #### Select the computer vision model
124 |
125 | Pick the model that you want Clarifai to use to analyse the images.
126 |
127 | 
128 |
129 | - [General](https://www.clarifai.com/models/image-recognition-ai) - concepts including objects, themes, moods and more
130 | - [Apparel](https://www.clarifai.com/models/clothing-image-recognition) - fashion-related concepts
131 | - [Celebrity](https://www.clarifai.com/models/celebrity-face-recognition) - recognised celebrities
132 | - [Color](https://www.clarifai.com/models/color-detection) - dominant colours present
133 | - [Food](https://www.clarifai.com/models/ai-food-recognition) - food items
134 | - [Moderation](https://www.clarifai.com/models/ai-image-moderation) - gore, drugs, explicit nudity or suggestive nudity
135 | - [NSFW](https://www.clarifai.com/models/nsfw-model-for-content-detection) - nudity
136 | - [Textures and Patterns](https://www.clarifai.com/models/texture-recognition) - common textures (feathers, woodgrain), unique/fresh concepts (petrified wood, glacial ice) and overarching descriptive concepts (veined, metallic)
137 | - [Travel](https://www.clarifai.com/models/travel-image-recognition) - specific features of residential, hotel and travel-related properties
138 |
139 | ### Open source models
140 |
141 | *Caution: The API that serves open source pre-trained computer vision models is experimental. It does not offer the same level of performance as the commercial APIs. The default endpoint in Memespector-GUI is for evaluation purpose only.*
142 |
143 | Check the box for `Open Source`.
144 |
145 | 
146 |
147 | #### Select the convolutional neural network (only if necessary)
148 |
149 | The open source models are made available by machine learning library [Keras](https://keras.io/). These models were trained on the [ImageNet database](https://www.image-net.org/). Just leave the default option unchanged unless you want to study these models. The differences between them are in the construction of convolutional neural networks. If interested in the details, see [here](https://keras.io/api/applications/).
150 |
151 | 
152 |
153 | ## Add images to Memespector-GUI
154 |
155 | ### Images on a computer
156 |
157 | Click `image files on this computer`. Select the image files. Click `Open`. The locations of the image files will be added to the `Image Sources` box.
158 |
159 | 
160 |
161 | 
162 |
163 | If you want to add all images inside a folder with many sub-folders which also contain images, click `a folder containing images on this computer`. Select the folder. The location of the folder will be added to the `Image Sources` box.
164 |
165 | 
166 |
167 | 
168 |
169 | *(Change of display behaviour from Version 0.2.3 beta: Only the location of the selected (outermost) folder will be displayed in the `Image Sources` box. The locations of image files inside the folder (and its subfolders) do not have to be displayed in the box in order to be processed. This change does not affect the total number of image files to be processed.)*
170 |
171 | ### Images on the web
172 |
173 | If you are going to process hundreds or thousands of URLs, paste the URLs into a text (.txt) file. Put one URL per line.
174 |
175 | 
176 |
177 | 
178 |
179 | Click `a text file containing image locations`. Select the text file. Click `Open`. The location of the text file containing the URLs will be added to the `Image Sources` box.
180 |
181 | 
182 |
183 | If you are going to just a few or tens of URLs, copy and paste the URLs of the images into the `Image Sources` box. Put one URL per line.
184 |
185 | 
186 |
187 | ## Edit the names for output files
188 |
189 | Memespector-GUI generates two output files. The JSON file stores the structured data values in full generated by the computer vision APIs. The CSV file contains a version of the data transformed into a tabular format which may be easily opened using general software tools like spreadsheet editors.
190 |
191 | Click `Browser` to the right of each field to change the name and location of an output file.
192 |
193 | 
194 |
195 | ## Invoke the APIs
196 |
197 | Click `Invoke APIs` after you have changed the APIs' settings, added image files or URLs to the `Image locations` box and/or edited the output filenames.
198 |
199 | 
200 |
201 | Memespector-GUI will show the progress of sending the images to the APIs.
202 |
203 | 
204 |
205 | Once all images are processed, a message about the completion will be shown. Open the output files to see the results.
206 |
207 | 
208 |
209 | # Credits
210 |
211 | Developed by [Jason Chao](https://jasontc.net/).
212 |
213 | Special thanks to [Janna Joceli Omena](https://github.com/jannajoceli) for her efforts in idea generation and software testing.
214 |
215 | Inspired by the original memespector projects of [bernorieder](https://github.com/bernorieder/memespector) and [amintz](https://github.com/amintz/memespector-python).
216 |
--------------------------------------------------------------------------------
/Memespector-GUI/MainWindow.axaml:
--------------------------------------------------------------------------------
1 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
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 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
--------------------------------------------------------------------------------
/Memespector-GUI/MainWindowViewModel.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Threading.Tasks;
3 | using Avalonia;
4 | using Avalonia.Controls;
5 | using MessageBox.Avalonia;
6 | using System.Reactive;
7 | using ReactiveUI;
8 | using System.Collections.Generic;
9 | using System.Linq;
10 | using System.IO;
11 | using MessageBox.Avalonia.Models;
12 | using Google.Cloud.Vision.V1;
13 | using CVClient;
14 | using Newtonsoft.Json;
15 | using ServiceStack.Text;
16 | using Memespector_GUI.APIViewModels;
17 | using Microsoft.Azure.CognitiveServices.Vision.ComputerVision.Models;
18 |
19 | namespace Memespector_GUI
20 | {
21 | public class MainWindowViewModel : ReactiveObject
22 | {
23 | public MainWindowViewModel()
24 | {
25 |
26 | BrowseFile = ReactiveCommand.Create(doBrowseFile);
27 | OpenExternal = ReactiveCommand.Create(doOpenExternal);
28 | ShowMessageBox = ReactiveCommand.Create(doShowMessageBox);
29 | InvokeTargetAPI = ReactiveCommand.Create(doInvokeTargetAPI);
30 |
31 | OutputJsonFileLocation = Path.Join(defaultDataPath, sessionBaseFilename + ".json");
32 | OutputCsvFileLocation = Path.Join(defaultDataPath, sessionBaseFilename + ".csv");
33 |
34 | this.WhenAnyValue(x => x.IsGoogleVisionEnabled, x => x.IsMicrosoftAuzreEnabled, x => x.IsClarifaiEnabled, x => x.IsOpenSourceEnabled).Subscribe(_ => saveUIStateToConfig());
35 | this.WhenAnyValue(x => x.GoogleVisionSettings.CredentialFileLocation, x => x.MicrosoftAzureSettings.Endpoint, x => x.MicrosoftAzureSettings.SubscriptionKey, x => x.ClarifaiSettings.APIKey, x => x.OpenSourceSettings.Endpoint).Subscribe(_ => saveUIStateToConfig());
36 | }
37 |
38 | private CVClientManager gvClient = new CVClientManager();
39 | public Window Parent { get; set; } = new Window(); // shall be set to the true parent (Window) of the view model. it is necessnary to pass the parent window to a message dialog box.
40 |
41 | public GoogleVisionSettingsViewModel GoogleVisionSettings { get; set; } = new GoogleVisionSettingsViewModel();
42 | public MicrosoftAzureSettingsViewModel MicrosoftAzureSettings { get; set; } = new MicrosoftAzureSettingsViewModel();
43 | public ClarifaiSettingsViewModel ClarifaiSettings { get; set; } = new ClarifaiSettingsViewModel();
44 | public OpenSourceSettingsViewModel OpenSourceSettings { get; set; } = new OpenSourceSettingsViewModel();
45 |
46 | public ReactiveCommand BrowseFile { get; }
47 | public ReactiveCommand OpenExternal { get; }
48 | public ReactiveCommand ShowMessageBox { get; }
49 | public ReactiveCommand InvokeTargetAPI { get; }
50 |
51 | private string defaultDataPath = Utilities.GetApplicationPath();
52 | private string sessionBaseFilename = string.Format("cv-apis-{0:yyyyMMdd_HHmmss}", DateTime.Now);
53 |
54 | private bool isGoogleVisionEnabled = Utilities.MemespectorConfig.SelectedGoogleVision;
55 | public bool IsGoogleVisionEnabled { get => isGoogleVisionEnabled; set => this.RaiseAndSetIfChanged(ref isGoogleVisionEnabled, value); }
56 |
57 | private bool isMicrosoftAuzreEnabled = Utilities.MemespectorConfig.SelectedMicrosoftAzure;
58 | public bool IsMicrosoftAuzreEnabled { get => isMicrosoftAuzreEnabled; set => this.RaiseAndSetIfChanged(ref isMicrosoftAuzreEnabled, value); }
59 |
60 | private bool isClarifaiEnabled = Utilities.MemespectorConfig.SelectedClarifai;
61 | public bool IsClarifaiEnabled { get => isClarifaiEnabled; set => this.RaiseAndSetIfChanged(ref isClarifaiEnabled, value); }
62 |
63 | private bool isOpenSourceEnabled = Utilities.MemespectorConfig.SelectedOpenSource;
64 | public bool IsOpenSourceEnabled { get => isOpenSourceEnabled; set => this.RaiseAndSetIfChanged(ref isOpenSourceEnabled, value); }
65 |
66 | private int imageSourcesCount = 0;
67 | private IEnumerable imageSources = new List();
68 | private string imageSourcesText = string.Empty;
69 | public string ImageSourcesText {
70 | get {
71 | if (imageSourcesCount > (Utilities.MemespectorConfig.MaxUIImageSourceLinesToDisplay) && Utilities.MemespectorConfig.MaxUIImageSourceLinesToDisplay > 0)
72 | return string.Join(Environment.NewLine, imageSources.Take(Utilities.MemespectorConfig.MaxUIImageSourceLinesToDisplay)) + $"{Environment.NewLine}... [truncated for display purpose only - {imageSourcesCount} lines in total - all will be processed]";
73 | else
74 | return imageSourcesText;
75 | } set {
76 | imageSources = value.Split(Environment.NewLine).Select(l => l.Trim()).Distinct().Where(l => (Utilities.IsFilePath(l) || Utilities.IsFolderPath(l) || Utilities.IsUrl(l)) && !string.IsNullOrEmpty(l));
77 | imageSourcesCount = imageSources.Count();
78 | this.RaiseAndSetIfChanged(ref imageSourcesText, string.Join(Environment.NewLine, imageSources));
79 | }
80 | }
81 |
82 | private string outputJsonFileLocation = string.Empty;
83 | public string OutputJsonFileLocation { get => outputJsonFileLocation; set => this.RaiseAndSetIfChanged(ref outputJsonFileLocation, value); }
84 | private string outputCsvFileLocation = string.Empty;
85 | public string OutputCsvFileLocation { get => outputCsvFileLocation; set => this.RaiseAndSetIfChanged(ref outputCsvFileLocation, value); }
86 |
87 | private int progressValue = 0;
88 | public int ProgressValue { get => progressValue; set => this.RaiseAndSetIfChanged(ref progressValue, value); }
89 |
90 | private string progressMessage = string.Empty;
91 | public string ProgressMessage { get => progressMessage; set => this.RaiseAndSetIfChanged(ref progressMessage, value); }
92 |
93 | private bool isInvocationInProgress = false;
94 | public bool IsInvocationInProgress { get => isInvocationInProgress; set => this.RaiseAndSetIfChanged(ref isInvocationInProgress, value); }
95 |
96 | private bool isInputEnabled = true;
97 | public bool IsInputEnabled { get => isInputEnabled; set => this.RaiseAndSetIfChanged(ref isInputEnabled, value); }
98 |
99 | private object outputFileIO = new object();
100 |
101 | private void saveUIStateToConfig()
102 | {
103 | var config = Utilities.MemespectorConfig;
104 |
105 | config.GoogleCloudCredentialFileLocation = GoogleVisionSettings.CredentialFileLocation;
106 | config.MicrosoftAzureEndpoint = MicrosoftAzureSettings.Endpoint;
107 | config.MicrosoftAzureSubscriptionKey = MicrosoftAzureSettings.SubscriptionKey;
108 | config.ClarifaiAPIKey = ClarifaiSettings.APIKey;
109 | config.OpenSourceEndpoint = OpenSourceSettings.Endpoint;
110 |
111 | config.SelectedGoogleVision = isGoogleVisionEnabled;
112 | config.SelectedMicrosoftAzure = isMicrosoftAuzreEnabled;
113 | config.SelectedClarifai = isClarifaiEnabled;
114 | config.SelectedOpenSource = isOpenSourceEnabled;
115 |
116 | Utilities.WriteConfig(config);
117 | }
118 |
119 | private void doOpenExternal(string parameter)
120 | {
121 | if (parameter == "Github")
122 | {
123 | Utilities.OpenExternalUrl("https://github.com/jason-chao/memespector-gui");
124 | }
125 | }
126 |
127 | private async void doShowMessageBox(string parameter)
128 | {
129 | if (parameter == "WebImages")
130 | {
131 | Utilities.ShowInfoDialog("Add images from the web", $"If you are going to add hundreds or even thousands of images from the web, please copy the URLs of the images and paste them into a text file (.txt). Then, add the text file into the box using the 'a text file containing image locations' button.{Environment.NewLine + Environment.NewLine}If you are going to add tens of or just a few images from the web, please paste the URLs directly into the box. Put one URL per line. You may press ENTER to create a new line.", Parent);
132 | }
133 | else if (parameter == "About")
134 | {
135 | var messageBox = MessageBoxManager.GetMessageBoxHyperlinkWindow(new MessageBox.Avalonia.DTO.MessageBoxHyperlinkParams
136 | {
137 | ButtonDefinitions = MessageBox.Avalonia.Enums.ButtonEnum.Ok,
138 | ContentTitle = "About Memespector GUI",
139 | HyperlinkContentProvider = new[] {
140 | new HyperlinkContent { Alias = "GUI client for computer vision APIs by " },
141 | new HyperlinkContent { Alias = "Jason CHAO", Url="https://jasontc.net/" }, new HyperlinkContent { Alias = ". " },
142 | new HyperlinkContent { Alias = "Inspired by the original command-line memespector projects of " },
143 | new HyperlinkContent { Alias = "bernorieder ", Url = "https://github.com/bernorieder/memespector" },
144 | new HyperlinkContent { Alias = "and " },
145 | new HyperlinkContent { Alias = "amintz", Url = "https://github.com/amintz/memespector-python" } },
146 | Icon = MessageBox.Avalonia.Enums.Icon.Info,
147 | WindowStartupLocation = WindowStartupLocation.CenterScreen,
148 | Style = MessageBox.Avalonia.Enums.Style.Windows
149 | });
150 | await messageBox.ShowDialog(Parent);
151 | }
152 | }
153 |
154 | private List getSelectedMicrosoftAzureFeatures()
155 | {
156 | var list = new List();
157 |
158 | if (MicrosoftAzureSettings.Detection_Adult)
159 | list.Add(VisualFeatureTypes.Adult);
160 |
161 | if (MicrosoftAzureSettings.Detection_Brands)
162 | list.Add(VisualFeatureTypes.Brands);
163 |
164 | if (MicrosoftAzureSettings.Detection_Categories)
165 | list.Add(VisualFeatureTypes.Categories);
166 |
167 | if (MicrosoftAzureSettings.Detection_Description)
168 | list.Add(VisualFeatureTypes.Description);
169 |
170 | if (MicrosoftAzureSettings.Detection_Faces)
171 | list.Add(VisualFeatureTypes.Faces);
172 |
173 | if (MicrosoftAzureSettings.Detection_Objects)
174 | list.Add(VisualFeatureTypes.Objects);
175 |
176 | if (MicrosoftAzureSettings.Detection_Tags)
177 | list.Add(VisualFeatureTypes.Tags);
178 |
179 | return list;
180 | }
181 |
182 | private List getSelectedGoogleVisionFeatures()
183 | {
184 | var list = new List();
185 |
186 | if (GoogleVisionSettings.Detection_Safety)
187 | list.Add(Feature.Types.Type.SafeSearchDetection);
188 |
189 | if (GoogleVisionSettings.Detection_Face)
190 | list.Add(Feature.Types.Type.FaceDetection);
191 |
192 | if (GoogleVisionSettings.Detection_Label)
193 | list.Add(Feature.Types.Type.LabelDetection);
194 |
195 | if (GoogleVisionSettings.Detection_Web)
196 | list.Add(Feature.Types.Type.WebDetection);
197 |
198 | if (GoogleVisionSettings.Detection_Text)
199 | list.Add(Feature.Types.Type.TextDetection);
200 |
201 | if (GoogleVisionSettings.Detection_Landmark)
202 | list.Add(Feature.Types.Type.LandmarkDetection);
203 |
204 | if (GoogleVisionSettings.Detection_Logo)
205 | list.Add(Feature.Types.Type.LogoDetection);
206 |
207 | return list;
208 | }
209 |
210 | private async void doInvokeTargetAPI(string parameter)
211 | {
212 | var settings = GoogleVisionSettings;
213 |
214 | if (parameter == "AllAPIs")
215 | {
216 | IsInputEnabled = false;
217 |
218 | if ((new bool[] { isGoogleVisionEnabled, isMicrosoftAuzreEnabled, isClarifaiEnabled, isOpenSourceEnabled }).Where(apiEnabled => apiEnabled).Count() <= 0)
219 | {
220 | Utilities.ShowErrorDialog("API settings", "No API is selected. Please select at least one API.", Parent);
221 | IsInputEnabled = true;
222 | return;
223 | }
224 |
225 | if (isGoogleVisionEnabled)
226 | {
227 | if (!File.Exists(GoogleVisionSettings.CredentialFileLocation))
228 | {
229 | Utilities.ShowErrorDialog("API settings", "The Google Cloud credential file has not been chosen or does not exist. Please select a credential file.", Parent);
230 | IsInputEnabled = true;
231 | return;
232 | }
233 |
234 | if (!CVClientCommon.Google_MayBeAValidCredentialFile(GoogleVisionSettings.CredentialFileLocation))
235 | {
236 | Utilities.ShowErrorDialog("API settings", "The Google Cloud credential file is invalid. Please select a valid credential file.", Parent);
237 | IsInputEnabled = true;
238 | return;
239 | }
240 |
241 | gvClient.GoogleCloudCredentialsFile = GoogleVisionSettings.CredentialFileLocation;
242 | }
243 |
244 | if (isMicrosoftAuzreEnabled)
245 | {
246 | if (string.IsNullOrEmpty(MicrosoftAzureSettings.SubscriptionKey))
247 | {
248 | Utilities.ShowErrorDialog("API settings", "The Microsoft Azure subscription key is missing. Please provide a subscription key.", Parent);
249 | IsInputEnabled = true;
250 | return;
251 | }
252 |
253 | gvClient.MicrosoftAzureSubscriptionKey = MicrosoftAzureSettings.SubscriptionKey;
254 |
255 | if (!Uri.IsWellFormedUriString(MicrosoftAzureSettings.Endpoint, UriKind.Absolute))
256 | {
257 | Utilities.ShowErrorDialog("API settings", "The Microsoft Azure Cognitive Services endpoint is invalid. Please provide a valid endpoint.", Parent);
258 | IsInputEnabled = true;
259 | return;
260 | }
261 |
262 | gvClient.MicrosoftAzureEndpoint = MicrosoftAzureSettings.Endpoint;
263 | }
264 |
265 | if (isClarifaiEnabled)
266 | {
267 | if (string.IsNullOrEmpty(ClarifaiSettings.APIKey))
268 | {
269 | Utilities.ShowErrorDialog("API settings", "The Clarifai API key is missing. Please provide an API key.", Parent);
270 | IsInputEnabled = true;
271 | return;
272 | }
273 |
274 | gvClient.ClarifaiApiKey = ClarifaiSettings.APIKey;
275 | }
276 |
277 | if (isOpenSourceEnabled)
278 | {
279 | if (!Uri.IsWellFormedUriString(OpenSourceSettings.Endpoint, UriKind.Absolute))
280 | {
281 | Utilities.ShowErrorDialog("API settings", "The open source API endpoint is missing. Please provide a valid endpoint.", Parent);
282 | IsInputEnabled = true;
283 | return;
284 | }
285 |
286 | gvClient.OpenSourceApiEndpoint = OpenSourceSettings.Endpoint;
287 | }
288 |
289 | if (File.Exists(outputJsonFileLocation) || File.Exists(outputCsvFileLocation))
290 | {
291 | var dialogResult = await Utilities.ShowOkCancelDialog("Exisiting output file", "The name for the ouput JSON or CSV file already exists. The file will be overwritten. Are you sure you want to continue?", Parent);
292 |
293 | if (dialogResult == MessageBox.Avalonia.Enums.ButtonResult.Cancel)
294 | {
295 | IsInputEnabled = true;
296 | return;
297 | }
298 | }
299 |
300 | var effecitveImageLocations = getEffectiveImageLocations(this.imageSources, true);
301 |
302 | if (!effecitveImageLocations.Any())
303 | {
304 | Utilities.ShowErrorDialog("Image locations", "Please provide valid locations of image files on this computer or on the web.", Parent);
305 | IsInputEnabled = true;
306 | return;
307 | }
308 |
309 | var googleVisionFeatureList = getSelectedGoogleVisionFeatures();
310 | var googleVisionMaxResults = Utilities.GetConfigGoogleVisionMaxResults();
311 | var googleVisionMinScores = Utilities.GetConfigGoogleVisionFlatteningMinScores();
312 |
313 | var microsoftAuzreFeatureList = getSelectedMicrosoftAzureFeatures();
314 | var microsoftAzureMinScores = Utilities.GetConfigMicrosoftAzureFlatteningMinScores();
315 |
316 | var memespecotrConfig = Utilities.MemespectorConfig;
317 |
318 | gvClient.ParallelCVImageTasksToRun = memespecotrConfig.MaxConcurrentImageTasks;
319 |
320 | var gvTaskList = new List();
321 |
322 | foreach (var fileLocation in effecitveImageLocations)
323 | {
324 | var gvTask = new CVImageTask() { ImageLocation = fileLocation };
325 |
326 | if (isGoogleVisionEnabled)
327 | {
328 | gvTask.GoogleInvocation = new InvocationGoogle() { DetectionFeatureTypes = googleVisionFeatureList, DetectionMaxResults = googleVisionMaxResults, FlatteningMinScores = googleVisionMinScores };
329 | }
330 |
331 | if (isMicrosoftAuzreEnabled)
332 | {
333 | gvTask.AzureInvocation = new InvocationAzure() { DetectionFeatureTypes = microsoftAuzreFeatureList, FlatteningMinScores = microsoftAzureMinScores };
334 | }
335 |
336 | if (isClarifaiEnabled)
337 | {
338 | gvTask.ClarifaiInvocation = new InvocationClarifai() { Model = ClarifaiSettings.Model, FlatteningMinScore = memespecotrConfig.ClarifaiMinScore };
339 | }
340 |
341 | if (isOpenSourceEnabled)
342 | {
343 | gvTask.OpenSourceInvocation = new InvocationOpenSource() { Model = OpenSourceSettings.Model, DetectionMaxResults = memespecotrConfig.OpenSourceMaxResults, FlatteningMinScore = memespecotrConfig.OpenSourceMinScore };
344 | }
345 |
346 | if (Utilities.IsFilePath(gvTask.ImageLocation))
347 | gvTask.ImageLocationType = CVClientCommon.LocationType.Local;
348 | else if (Utilities.IsUrl(gvTask.ImageLocation))
349 | gvTask.ImageLocationType = CVClientCommon.LocationType.Uri;
350 |
351 | gvTaskList.Add(gvTask);
352 | }
353 |
354 | IsInvocationInProgress = true;
355 |
356 | var checkProgressTask = Task.Run(() =>
357 | {
358 | int total = gvTaskList.Count;
359 | int processed = 0;
360 | int written = 0;
361 | DateTime lastWritten = DateTime.Now;
362 | do
363 | {
364 | processed = gvTaskList.Count(t => t.Completed.HasValue);
365 | if (processed > written)
366 | {
367 | if ((DateTime.Now - lastWritten).TotalSeconds > memespecotrConfig.MinWriteResultsIntervalInSeconds)
368 | {
369 | writeResultsToOutputFiles(gvTaskList);
370 | written = processed;
371 | lastWritten = DateTime.Now;
372 | }
373 | }
374 | ProgressValue = Convert.ToInt32(Convert.ToDouble(processed) / Convert.ToDouble(total) * 100);
375 | ProgressMessage = $"Processed {processed.ToString()} of {total.ToString()}";
376 | System.Threading.Thread.Sleep(memespecotrConfig.MinUIProgressUpdateIntervalInMilliseconds);
377 | } while (!((processed >= total) || !IsInvocationInProgress));
378 | });
379 |
380 | bool completed = false;
381 | Exception? runTaskException = null;
382 |
383 | try
384 | {
385 | await gvClient.RunTasks(gvTaskList);
386 | completed = true;
387 | }
388 | catch (Exception ex) { runTaskException = ex; }
389 | finally { writeResultsToOutputFiles(gvTaskList); }
390 |
391 | IsInputEnabled = true;
392 | IsInvocationInProgress = false;
393 |
394 | if (completed)
395 | Utilities.ShowInfoDialog("Completion", "All images have been processed by the APIs. Open the result files to see the details.", Parent);
396 | else
397 | {
398 | string exceptionMessage = (runTaskException != null) ? Environment.NewLine + Environment.NewLine + "Technical message: " + runTaskException.Message + $" ({runTaskException.Source})" : string.Empty;
399 | Utilities.ShowInfoDialog("Completion", $"Some images may NOT have been processed. Open the result files to see the details.{exceptionMessage}", Parent);
400 | }
401 | }
402 |
403 | }
404 |
405 | private void writeResultsToOutputFiles(IEnumerable cvImageTasks)
406 | {
407 | lock (outputFileIO)
408 | {
409 | File.WriteAllText(outputJsonFileLocation, JsonConvert.SerializeObject(cvImageTasks, Formatting.Indented));
410 | File.WriteAllText(OutputCsvFileLocation, CsvSerializer.SerializeToCsv(cvImageTasks.Select(t => t.FlatResults)));
411 | }
412 | }
413 |
414 | private IEnumerable getEffectiveImageLocations(IEnumerable locations, bool parseTxt = false)
415 | {
416 | var effecitveLocationList = new List();
417 | var imageExtensions = CVClientCommon.SupportedFormats;
418 |
419 | foreach (var location in locations)
420 | {
421 | if (string.IsNullOrEmpty(location))
422 | continue;
423 |
424 | if (Utilities.IsFolderPath(location))
425 | {
426 | if (!Directory.Exists(location))
427 | continue;
428 |
429 | var locationsInFolder = getEffectiveImageLocations(Directory.GetFiles(location, "*.*", SearchOption.AllDirectories).ToArray(), false);
430 | effecitveLocationList.AddRange(locationsInFolder);
431 | }
432 | else if (Utilities.IsFilePath(location))
433 | {
434 | if (!File.Exists(location))
435 | continue;
436 |
437 | if (parseTxt && location.ToLower().EndsWith(".txt"))
438 | {
439 | var locationsInFile = getEffectiveImageLocations(File.ReadAllLines(location), false);
440 | effecitveLocationList.AddRange(locationsInFile);
441 | }
442 | else if (imageExtensions.Any(ext => location.ToLower().EndsWith("." + ext)))
443 | {
444 | effecitveLocationList.Add(location);
445 | }
446 | }
447 | else if (Utilities.IsUrl(location))
448 | effecitveLocationList.Add(location); // IsURL must be the last case to be tested. A file or folder path is also considered an absolute path.
449 | }
450 |
451 | return effecitveLocationList.Distinct();
452 | }
453 |
454 |
455 | private async void doBrowseFile(string forProperty)
456 | {
457 | if (forProperty == "GCSCredentialFileLocation")
458 | {
459 | var openFileDialog = new OpenFileDialog() { Title = "Open Google Cloud credential file", AllowMultiple = false, Filters = new List { new FileDialogFilter { Name = "JSON", Extensions = { "json" } } }, Directory = defaultDataPath };
460 | var files = await openFileDialog.ShowAsync(Parent);
461 | if (files.Any())
462 | {
463 | string filename = files.First();
464 | GoogleVisionSettings.CredentialFileLocation = filename;
465 | }
466 | }
467 | else if (forProperty == "OutputJsonFileLocation")
468 | {
469 | var saveFileDialog = new SaveFileDialog() { Title = "Save detailed results as JSON", InitialFileName = Path.GetFileName(OutputJsonFileLocation), Filters = new List { new FileDialogFilter { Name = "JSON", Extensions = { "json" } } }, Directory = defaultDataPath };
470 | var file = await saveFileDialog.ShowAsync(Parent);
471 | if (!string.IsNullOrEmpty(file))
472 | OutputJsonFileLocation = file;
473 | }
474 | else if (forProperty == "OutputCsvFileLocation")
475 | {
476 | var saveFileDialog = new SaveFileDialog() { Title = "Save simplified results as CSV", InitialFileName = Path.GetFileName(OutputCsvFileLocation), Filters = new List { new FileDialogFilter { Name = "CSV", Extensions = { "csv" } } }, Directory = defaultDataPath };
477 | var file = await saveFileDialog.ShowAsync(Parent);
478 | if (!string.IsNullOrEmpty(file))
479 | OutputCsvFileLocation = file;
480 | }
481 | else if (forProperty.StartsWith("ImageFileLocations_"))
482 | {
483 | var imageExtensions = CVClientCommon.SupportedFormats;
484 | string[] localPaths = new string[] { };
485 |
486 | if (forProperty.EndsWith("_Files"))
487 | {
488 | var openFileDialog = new OpenFileDialog() { Title = "Open image files", AllowMultiple = true, Filters = new List { new FileDialogFilter { Name = "Image", Extensions = imageExtensions } }, Directory = defaultDataPath };
489 | localPaths = await openFileDialog.ShowAsync(Parent);
490 | }
491 | else if (forProperty.EndsWith("_Txt"))
492 | {
493 | var openFileDialog = new OpenFileDialog() { Title = "Open a text file containing image locations", AllowMultiple = false, Filters = new List { new FileDialogFilter { Name = "Text", Extensions = new List() { "txt" } } }, Directory = defaultDataPath };
494 | localPaths = await openFileDialog.ShowAsync(Parent);
495 | }
496 | else if (forProperty.EndsWith("_Folder"))
497 | {
498 | var openFolderDialog = new OpenFolderDialog() { Title = "Open a folder containing images", Directory = defaultDataPath };
499 | localPaths = new string[] { await openFolderDialog.ShowAsync(Parent) };
500 | }
501 |
502 | if (localPaths.Any())
503 | {
504 | ImageSourcesText = string.Join(Environment.NewLine, localPaths) + Environment.NewLine + imageSourcesText;
505 | }
506 | }
507 | }
508 | }
509 | }
510 |
--------------------------------------------------------------------------------