Wednesday, October 25, 2023

Accessing CodeProject's AI Explorer from WPF

I got an email from CodeProject about their AI Explorer that can be hosted locally.

https://www.codeproject.com/Articles/5322557/CodeProject-AI-Server-AI-the-easy-way

It took about an hour to install it and not all the modules are usable and useful. Nevertheless, the modules that are useful are interesting. It comes with a well designed dashboard that lets you interact with the AI services.

Once you have installed this and installed the scene classification module you will see Scene Classification as it looks below. 


These services can also be called from your code. I wanted to write a simple WPF application that could classify images as you can see above. My application won't be as pretty, but it will be mine.

Start a new C#, .Net Core Visual Studio project and call it SceneClassifier.

It will let the user select an image, and when they do it will display the image and call the SceneClassifier AI service. It will then display the scene classification and confidence.

Here's the XAML for MainWindow.xaml. There's nothing clever here. It's all in the code.

<Window x:Class="SceneClassifier.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:SceneClassifier"
        mc:Ignorable="d"
        DataContext="{Binding RelativeSource={RelativeSource Self}}"
        SizeToContent="WidthAndHeight"
        Title="Scene Classifier" Height="450" Width="800">
    <Window.Resources>
        <RoutedCommand x:Key="SelectCommand"/>
    </Window.Resources>
    <Window.CommandBindings>
        <CommandBinding Command="{StaticResource SelectCommand}" Executed="Select_Executed"/>
    </Window.CommandBindings>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="auto"/>
            <RowDefinition Height="auto"/>
            <RowDefinition Height="auto"/>
            <RowDefinition Height="auto"/>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="100"/>
            <ColumnDefinition Width="100"/>
            <ColumnDefinition Width="100"/>
            <ColumnDefinition Width="100"/>
        </Grid.ColumnDefinitions>
 
        <Button Grid.Row="0" Grid.Column="0" Content="Select Image" Command="{StaticResource SelectCommand}"/>
        <TextBlock Grid.Row="0" Grid.Column="1" Grid.ColumnSpan="3" Text="{Binding FileName}"/>
 
        <Image Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="4" Height="200" Source="{Binding FileName}"/>
 
        <TextBlock Grid.Row="2" Grid.Column="0" Text="Classification:"/>
        <TextBlock Grid.Row="2" Grid.Column="1" Text="{Binding Classification}"/>
 
        <TextBlock Grid.Row="2" Grid.Column="2" Text="Confidence"/>
        <TextBlock Grid.Row="2" Grid.Column="3" Text="{Binding Confidence}"/>
       
        <TextBlock Grid.Row="3" Grid.Column="0" Grid.ColumnSpan="4" Text="{Binding Status}"/>
    </Grid>
</Window>

-------------------------------------------------------------------------------------

The code uses an HttpClient to access the REST service provided by CodeProject's AI server and deserializes the result into an object. The we pull the classification and confidence out of the object. Yes, I know the architecture sucks - this isn't a post about code purity :-)

using CommunityToolkit.Mvvm.ComponentModel;
using Microsoft.Win32;
using System;
using System.Net.Http;
using System.Windows;
using System.Windows.Input;
using System.Net.Http.Json;
 
namespace SceneClassifier;
public class cResult
{
    public bool success { get; set; }
    public string label { get; set; }
    public float confidence { get; set; }
    public string command { get; set; }
    public string moduleId { get; set; }
    public string executionProvider { get; set; }
    public bool canUseGPU { get; set; }
    public int inferenceMs { get; set; }
    public int processMs { get; set; }
    public int analysisRoundTripMs { get; set; }
}
 
[ObservableObject]
public partial class MainWindow : Window
{
    [ObservableProperty] String _FileName = "";
    [ObservableProperty] String _Status = "";
    [ObservableProperty] string _Classification;
    [ObservableProperty] String _Confidence;
 
    HttpClient httpClient = new HttpClient();
 
    public MainWindow()
    {
        InitializeComponent();
    }
 
    private void Select_Executed(object sender, ExecutedRoutedEventArgs e)
    {
        OpenFileDialog openFileDialog = new OpenFileDialog();
        openFileDialog.Filter = "png Files|*.png";
        openFileDialog.InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.MyPictures);
        if (openFileDialog.ShowDialog().Value)
        {
            FileName = openFileDialog.FileName;
            ClassifyScene(FileName);
        }
    }
 
    private async void ClassifyScene(string FileName)
    {
        Byte[] imageBytes = System.IO.File.ReadAllBytes(FileName);
        ByteArrayContent content = new ByteArrayContent(imageBytes);
        content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("image/png");
 
        MultipartFormDataContent form = new MultipartFormDataContent();
        form.Add(content, "image", "image.png");
 
        HttpResponseMessage response = await httpClient.PostAsync("http://localhost:32168/v1/vision/scene", form);
        if (response.IsSuccessStatusCode)
        {
            cResult result = await response.Content.ReadFromJsonAsync<cResult>();
            Classification = result.label;
            Confidence = Math.Round(result.confidence * 100).ToString() + "%";
            Status = "Success";
        }
        else
        {
            string Error = await response.Content.ReadAsStringAsync();
            Status = $"Error: {response.StatusCode}:{Error}";
        }
    }
}



 


No comments:

Post a Comment