├── 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 | ![Screenshot](res/cl-create-application.jpg) 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 | ![Screenshot](res/cl-application-naming.jpg) 14 | 15 | Click the tile showing application you created just now. 16 | 17 | ![Screenshot](res/cl-application-created.jpg) 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 | ![Screenshot](res/cl-api-key.jpg) 22 | 23 | ![Screenshot](res/gui-cl-api-key.jpg) 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 | ![Screenshot](res/ma-signin.jpg) 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 | ![Screenshot](res/ma-add-a-resource.jpg) 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 | ![Screenshot](res/ma-resource-search.jpg) 20 | 21 | Click `Create`. 22 | 23 | ![Screenshot](res/ma-cognitive-create.jpg) 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 | ![Screenshot](res/ma-res-group-naming.jpg) 28 | 29 | Select a `Region` as close as possible to your physical location. 30 | 31 | ![Screenshot](res/ma-region-selection.jpg) 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 | ![Screenshot](res/ma-cogserv-naming.jpg) 36 | 37 | Click `Create`. 38 | 39 | ![Screenshot](res/ma-confirm-create.jpg) 40 | 41 | Click `Go to resource`. 42 | 43 | ![Screenshot](res/ma-go-to-resource.jpg) 44 | 45 | Click `Keys and Endpoint` on the left. 46 | 47 | ![Screenshot](res/ma-keys-and-endpoint.jpg) 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 | ![Screenshot](res/ma-keys-endpoint-copy.jpg) 52 | 53 | ![Screenshot](res/gui-ma-key-endpoint.jpg) 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 | ![Screenshot](res/firstrun-windows-exe.jpg) 16 | 17 | If you see a window telling you not to run the app, click `More info`. 18 | 19 | ![Screenshot](res/firstrun-windows-moreinfo.jpg) 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 | ![Screenshot](res/firstrun-windows-runanyway.jpg) 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 | ![Screenshot](res/firstrun-mac-exe.jpg) 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 | ![Screenshot](res/firstrun-mac-openrelease.jpg) 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 | ![Screenshot](res/firstrun-mac-cannotrun.jpg) 41 | 42 | Open [System Preferences](https://support.apple.com/guide/macbook-pro/system-preferences-apda966cb8af/mac) on your Mac. Click `Security & Privacy`. 43 | 44 | ![Screenshot](res/firstrun-mac-systempreference.jpg) 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 | ![Screenshot](res/firstrun-mac-securitygeneral.jpg) 49 | 50 | Click `Open`. 51 | 52 | ![Screenshot](res/firstrun-mac-openanyway.jpg) 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 | ![Screenshot](res/gcp-activate.jpg) 8 | 9 | Choose your country and check the box to agree to the service terms. Click `CONTINUE`. 10 | 11 | ![Screenshot](res/gcp-signup-step1.jpg) 12 | 13 | Complete the form with your name, address and details of your payment card. 14 | 15 | ![Screenshot](res/gcp-signup-step2.1.jpg) 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 | ![Screenshot](res/gcp-signup-step2.2.jpg) 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 | ![Screenshot](res/gcp-project-new.jpg) 33 | 34 | Give a name to the project. Click `CREATE`. 35 | 36 | ![Screenshot](res/gcp-project-naming.jpg) 37 | 38 | ## Enable Vision API 39 | 40 | Click the sandwich menu button on the top left. Navigate to `API & Services` -> `Library`. 41 | 42 | ![Screenshot](res/gcp-enableapi-navigate.jpg) 43 | 44 | Click `Cloud Vision API` 45 | 46 | ![Screenshot](res/gcp-enableapi-select.jpg) 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 | ![Screenshot](res/gcp-enableapi-search.jpg) 51 | 52 | Click `Enable` 53 | 54 | ![Screenshot](res/gcp-enableapi-enable.jpg) 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 | ![Screenshot](res/gcp-credential-navigate.jpg) 61 | 62 | Click `CREATE SERVICE ACCOUNT`. 63 | 64 | ![Screenshot](res/gcp-credential-create.jpg) 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 | ![Screenshot](res/gcp-credential-naming.jpg) 69 | 70 | Click `Select a role` 71 | 72 | ![Screenshot](res/gcp-credential-role1.jpg) 73 | 74 | Navigate to `Basic` -> `Owner`. 75 | 76 | ![Screenshot](res/gcp-credential-role2.jpg) 77 | 78 | Click `CONTINUE`. 79 | 80 | ![Screenshot](res/gcp-credential-role3.jpg) 81 | 82 | Leave these fields blank. Click `DONE`. 83 | 84 | ![Screenshot](res/gcp-credential-done.jpg) 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 | ![Screenshot](res/gcp-credential-createkey.jpg) 89 | 90 | Select `JSON` for `Key type`. Click `CREATE`. 91 | 92 | ![Screenshot](res/gcp-credential-createjson.jpg) 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 | ![Screenshot](res/gcp-credential-downloadjson.jpg) 97 | 98 | In Memespector GUI, click `Browse` and select the downloaded .json file as the `Credential file` for Google Vision. 99 | 100 | ![Screenshot](res/gui-gcp-credential-file.jpg) 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 [![DOI](https://zenodo.org/badge/333639175.svg)](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 | ![Screenshot](doc/res/memespector-gui-screenshot-mac.png) 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 | ![Screenshot](doc/res/gui-win-settings.jpg) 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 | ![Screenshot](doc/res/gui-gv-credential-file-field.jpg) 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 | ![Screenshot](doc/res/gui-gv-win-selectcredentialfile.jpg) 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 | ![Screenshot](doc/res/gui-gv-feature-selection.jpg) 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 | ![screenshot](doc/res/gui-az-endpoint-key-fields.jpg) 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 | ![screenshot](doc/res/gui-az-feature-selection.jpg) 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 | ![screenshot](doc/res/gui-cl-api-key-field.jpg) 122 | 123 | #### Select the computer vision model 124 | 125 | Pick the model that you want Clarifai to use to analyse the images. 126 | 127 | ![screenshot](doc/res/gui-cl-model-selection.jpg) 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 | ![screenshot](doc/res/gui-os-endpoint.jpg) 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 | ![screenshot](doc/res/gui-os-model-selection.jpg) 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 | ![Screenshot](doc/res/gui-win-individual-files.jpg) 160 | 161 | ![Screenshot](doc/res/gui-win-individual-file-locations.jpg) 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 | ![Screenshot](doc/res/gui-win-a-folder.jpg) 166 | 167 | ![Screenshot](doc/res/gui-win-a-folder-locations.jpg) 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 | ![Screenshot](doc/res/gui-web-urls-txt.jpg) 176 | 177 | ![Screenshot](doc/res/gui-win-txt-file.jpg) 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 | ![Screenshot](doc/res/gui-win-a-txt-file-location.jpg) 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 | ![Screenshot](doc/res/gui-win-web-locations.jpg) 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 | ![Screenshot](doc/res/gui-win-outputfiles.jpg) 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 | ![Screenshot](doc/res/gui-win-invokebutton.jpg) 200 | 201 | Memespector-GUI will show the progress of sending the images to the APIs. 202 | 203 | ![Screenshot](doc/res/gui-win-progress.jpg) 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 | ![Screenshot](doc/res/gui-win-invoke-completion.jpg) 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 | 34 | 35 |