Thursday, July 2, 2026

Build an Image to Text Extractor using OpenAi

Build an Image-to-Text Extractor in C# Using OpenAI Vision API (.NET Console Application)

Need to extract text from images using C#? In this tutorial, you'll build a simple Image to Text Generator using the OpenAI Vision API and .NET. The application reads an image, sends it to OpenAI, and returns all visible text from the image.


Prerequisites

  • Visual Studio 2022
  • .NET 8 SDK
  • OpenAI API Key
  • Basic knowledge of C#

Project Structure

ImageToTextGenerator
│
├── Services
│   ├── IImageToTextService.cs
│   └── ImageToTextService.cs
│
├── Program.cs
├── appsettings.json
└── ImageToTextGenerator.csproj

Step 1: Create Interface (IImageToTextService.cs)

Create a new interface inside the Services folder.

namespace ImageToTextGenerator.Services;

public interface IImageToTextService
{
    Task<string> ExtractTextAsync(
        string imagePath,
        CancellationToken cancellationToken = default);
}

Step 2: Create ImageToTextService

Create a new class named ImageToTextService.cs inside the Services folder.

using System.Net.Http.Headers;
using System.Net.Http.Json;
using System.Text.Json;
using Microsoft.Extensions.Configuration;

namespace ImageToTextGenerator.Services;

public class ImageToTextService : IImageToTextService
{
    private readonly HttpClient _httpClient;
    private readonly string _modelId;
    private readonly string _apiKey;

    public ImageToTextService(
        HttpClient httpClient,
        IConfiguration configuration)
    {
        _httpClient = httpClient;

        _modelId = configuration["OpenAI:ModelId"]
            ?? throw new InvalidOperationException(
                "OpenAI:ModelId not found in appsettings.json.");

        _apiKey = configuration["OpenAI:ApiKey"]
            ?? throw new InvalidOperationException(
                "OpenAI:ApiKey not found in appsettings.json.");
    }

    public async Task<string> ExtractTextAsync(
        string imagePath,
        CancellationToken cancellationToken = default)
    {
        if (!File.Exists(imagePath))
            throw new FileNotFoundException(
                $"Image not found: {imagePath}",
                imagePath);

        var bytes = await File.ReadAllBytesAsync(
            imagePath,
            cancellationToken);

        var base64 = Convert.ToBase64String(bytes);

        var mimeType = GetMimeType(imagePath);

        var dataUrl = $"data:{mimeType};base64,{base64}";

        var payload = new
        {
            model = _modelId,
            messages = new object[]
            {
                new
                {
                    role = "user",
                    content = new object[]
                    {
                        new
                        {
                            type = "text",
                            text = "Extract all text visible in this image. Return only the text, no commentary."
                        },
                        new
                        {
                            type = "image_url",
                            image_url = new
                            {
                                url = dataUrl
                            }
                        }
                    }
                }
            }
        };

        using var request = new HttpRequestMessage(
            HttpMethod.Post,
            "chat/completions")
        {
            Content = JsonContent.Create(payload)
        };

        request.Headers.Authorization =
            new AuthenticationHeaderValue("Bearer", _apiKey);

        using var response = await _httpClient.SendAsync(
            request,
            cancellationToken);

        var body = await response.Content.ReadAsStringAsync(
            cancellationToken);

        if (!response.IsSuccessStatusCode)
            throw new HttpRequestException(
                $"OpenAI endpoint returned {(int)response.StatusCode}: {body}");

        using var doc = JsonDocument.Parse(body);

        return doc.RootElement
            .GetProperty("choices")[0]
            .GetProperty("message")
            .GetProperty("content")
            .GetString() ?? string.Empty;
    }

    private static string GetMimeType(string path)
        => Path.GetExtension(path).ToLowerInvariant() switch
        {
            ".jpg" or ".jpeg" => "image/jpeg",
            ".png" => "image/png",
            ".gif" => "image/gif",
            ".webp" => "image/webp",
            ".bmp" => "image/bmp",
            _ => throw new NotSupportedException(
                $"Unsupported image extension: {Path.GetExtension(path)}")
        };
}

Step 3: Program.cs

Configure dependency injection, load configuration, and call the image extraction service.

using ImageToTextGenerator.Services;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;

try
{
    var config = new ConfigurationBuilder()
        .SetBasePath(AppContext.BaseDirectory)
        .AddJsonFile(
            "appsettings.json",
            optional: false,
            reloadOnChange: false)
        .Build();

    var endpoint = config["OpenAI:Endpoint"]
        ?? throw new InvalidOperationException(
            "OpenAI:Endpoint not found in appsettings.json.");

    if (!Uri.TryCreate(
            endpoint,
            UriKind.Absolute,
            out var endpointUri))
    {
        throw new InvalidOperationException(
            $"OpenAI:Endpoint '{endpoint}' is not a valid absolute URI.");
    }

    var baseAddress = endpoint.EndsWith('/')
        ? endpointUri
        : new Uri(endpoint + "/");

    var services = new ServiceCollection();

    services.AddSingleton(config);

    services.AddHttpClient<IImageToTextService, ImageToTextService>(http =>
    {
        http.BaseAddress = baseAddress;
        http.Timeout = TimeSpan.FromMinutes(3);
    });

    await using var provider =
        services.BuildServiceProvider();

    string? imagePath =
        @"C:\Screenshot 2026-07-02 143404.png";

    if (string.IsNullOrWhiteSpace(imagePath))
        throw new InvalidOperationException(
            "No image path provided.");

    imagePath = imagePath.Trim().Trim('"');

    var service =
        provider.GetRequiredService<IImageToTextService>();

    var text = await service.ExtractTextAsync(imagePath);

    Console.WriteLine();
    Console.WriteLine("---- Extracted text ----");
    Console.WriteLine(text);

    return 0;
}
catch (FileNotFoundException ex)
{
    Console.Error.WriteLine($"File error: {ex.Message}");
    return 1;
}
catch (NotSupportedException ex)
{
    Console.Error.WriteLine($"Unsupported input: {ex.Message}");
    return 1;
}
catch (InvalidOperationException ex)
{
    Console.Error.WriteLine($"Configuration error: {ex.Message}");
    return 1;
}
catch (HttpRequestException ex)
{
    Console.Error.WriteLine($"HTTP error: {ex.Message}");
    return 1;
}
catch (TaskCanceledException)
{
    Console.Error.WriteLine("Request timed out (3 minutes).");
    return 1;
}
catch (Exception ex)
{
    Console.Error.WriteLine(
        $"Fatal error: {ex.GetType().Name}: {ex.Message}");
    return 1;
}

appsettings.json

Create an appsettings.json file in your project.

{
  "OpenAI": {
    "Endpoint": "https://api.openai.com/v1/",
    "ModelId": "gpt-4.1-mini",
    "ApiKey": "YOUR_OPENAI_API_KEY"
  }
}

How It Works

  1. Reads the image from disk.
  2. Converts the image into Base64.
  3. Creates a Data URL.
  4. Sends the image to the OpenAI Chat Completions API.
  5. Uses a Vision-capable model to read the image.
  6. Returns only the extracted text.

Sample Output

---- Extracted text ----

Invoice Number: INV-2026-105
Customer Name: John Smith
Date: 02 July 2026
Total Amount: $325.00

Conclusion

You have successfully built an Image to Text Generator in C# using the OpenAI Vision API. This approach works for screenshots, scanned documents, invoices, forms, receipts, and many other image types. You can further enhance the application by adding batch processing, PDF support, drag-and-drop, or exporting extracted text to Word, Excel, or a database.

Build a interactive chatbot using AI modal using SQL database with .NET Semantic Kernel plugin

Build an Interactive AI Chatbot Using SQL Server and .NET Semantic Kernel Plugins

Artificial Intelligence has transformed the way modern applications interact with users. Instead of creating countless APIs for every business operation, developers can now use Large Language Models (LLMs) together with Semantic Kernel plugins to allow AI to retrieve live information directly from enterprise databases.

In this tutorial, you'll learn how to build an Interactive Insurance Chatbot using .NET 8, Microsoft Semantic Kernel, SQL Server, Entity Framework Core, and OpenAI Chat Completion. The chatbot understands natural language questions and automatically calls a Semantic Kernel plugin whenever data needs to be fetched from SQL Server.

What You'll Build

By the end of this tutorial, you'll have an AI-powered console chatbot capable of answering questions such as:

  • Show all insurance customers.
  • Who has Medical Insurance?
  • List all Quarterly premium customers.
  • Show policies started in 2019.
  • What is Rohit Parmar's premium amount?
  • List all Life Insurance policies.

Unlike traditional chatbots that rely on predefined responses, this chatbot retrieves real-time information directly from SQL Server using Semantic Kernel's automatic function calling feature.

Solution Architecture

User
   │
   ▼
OpenAI Chat Model
   │
Semantic Kernel
   │
Insurance Plugin
   │
Insurance Business Layer
   │
Entity Framework Core
   │
SQL Server Database

Whenever the user asks a question related to insurance records, the AI automatically invokes the appropriate plugin, retrieves data from SQL Server, and generates a natural language response based on the returned records.

Technologies Used

  • .NET 8
  • Microsoft Semantic Kernel
  • OpenAI Chat Completion
  • Entity Framework Core
  • SQL Server
  • Dependency Injection
  • C#
  • Semantic Kernel Plugins

Prerequisites

Before starting this project, make sure you have the following installed:

  • Visual Studio 2022
  • .NET 8 SDK
  • SQL Server
  • SQL Server Management Studio (SSMS)
  • OpenAI API Key
  • Microsoft Semantic Kernel NuGet Packages
  • Entity Framework Core SQL Server Provider
Note: This tutorial uses Semantic Kernel Function Calling to automatically invoke database functions whenever the AI determines that external data is required to answer the user's question.

Step 1 – Prepare the Insurance Table with Mock Data

The first step is to create an Insurance table in SQL Server and insert a few sample records. These records will be queried by the chatbot through Entity Framework Core.


CREATE TABLE Insurance
(
    firstname           VARCHAR(100),
    lastname            VARCHAR(100),
    email               VARCHAR(100),
    contactNumber       VARCHAR(10),
    insurancetype       VARCHAR(100),
    insurancestartdate  DATE,
    premium             DECIMAL,
    premiumfrequency    VARCHAR(100)
);

INSERT INTO Insurance
(firstname, lastname, email, contactNumber, insurancetype, insurancestartdate, premium, premiumfrequency)
VALUES
('Rohit', 'Parmar', 'rohit.parmar@gmail.com', '8454042163', 'Term', '03-12-2016', '5656.00', 'Quaterly');

INSERT INTO Insurance
(firstname, lastname, email, contactNumber, insurancetype, insurancestartdate, premium, premiumfrequency)
VALUES
('Shailash', 'Singh', 'shailash.singh@gmail.com', '8158042151', 'Term', '03-12-2009', '15236.00', 'Half Yearly');

INSERT INTO Insurance
(firstname, lastname, email, contactNumber, insurancetype, insurancestartdate, premium, premiumfrequency)
VALUES
('Chinmay', 'Halankar', 'chinmay.halankar@gmail.com', '6454032121', 'Medical', '03-12-2018', '51626.00', 'Yearly');

INSERT INTO Insurance
(firstname, lastname, email, contactNumber, insurancetype, insurancestartdate, premium, premiumfrequency)
VALUES
('Malay', 'Mondal', 'malay.mondal@gmail.com', '7654048723', 'Term', '03-12-2019', '8923.00', 'Quaterly');

INSERT INTO Insurance
(firstname, lastname, email, contactNumber, insurancetype, insurancestartdate, premium, premiumfrequency)
VALUES
('Rupesh', 'Patil', 'rupesh.patil@gmail.com', '9454043400', 'Life Insurance', '03-12-2010', '3486.00', 'Half Yearly');

INSERT INTO Insurance
(firstname, lastname, email, contactNumber, insurancetype, insurancestartdate, premium, premiumfrequency)
VALUES
('Anjali', 'Shinde', 'angali.shinde@gmail.com', '9654090160', 'Endowment Plan', '03-12-2012', '7106.00', 'Quaterly');

Step 2 – Create the Insurance Model

Create Insurance.cs inside the Models folder.


using Microsoft.EntityFrameworkCore;
using System.ComponentModel.DataAnnotations.Schema;

namespace InteractiveChatBot.Models;

[Table("Insurance")]
[Keyless]
public class Insurance
{
    [Column("firstname")]
    public string? FirstName { get; set; }

    [Column("lastname")]
    public string? LastName { get; set; }

    [Column("email")]
    public string? Email { get; set; }

    [Column("contactNumber")]
    public string? ContactNumber { get; set; }

    [Column("insurancetype")]
    public string? InsuranceType { get; set; }

    [Column("insurancestartdate")]
    public DateTime? InsuranceStartDate { get; set; }

    [Column("premium")]
    public decimal? Premium { get; set; }

    [Column("premiumfrequency")]
    public string? PremiumFrequency { get; set; }
}

Step 3 – Create AppDbContext

Create AppDbContext.cs.



using InteractiveChatBot.Models;
using Microsoft.EntityFrameworkCore;

namespace InteractiveChatBot.Data;

public class AppDbContext : DbContext
{
    public AppDbContext(DbContextOptions options)
        : base(options)
    {
    }

    public DbSet Insurance => Set();
}

Step 4 – Create InsuranceBusiness.cs

The business layer retrieves insurance records using Entity Framework Core.


using InteractiveChatBot.Data;
using InteractiveChatBot.Models;
using Microsoft.EntityFrameworkCore;

namespace InteractiveChatBot.Business
{
    public class InsuranceBusiness
    {
        private readonly AppDbContext _appContext;

        public InsuranceBusiness(AppDbContext appContext)
        {
            _appContext = appContext;
        }

        public async Task> ReadInsuranceAsync()
        {
            return await _appContext.Insurance
                .AsNoTracking()
                .ToListAsync();
        }
    }
}

Step 5 – Create InsurancePlugin.cs

The Semantic Kernel plugin exposes the database method as an AI function.


using System.ComponentModel;
using InteractiveChatBot.Business;
using InteractiveChatBot.Models;
using Microsoft.SemanticKernel;

namespace InteractiveChatBot.Plugins;

public class InsurancePlugin
{
    private readonly InsuranceBusiness _business;

    public InsurancePlugin(InsuranceBusiness business)
    {
        _business = business;
    }

    [KernelFunction("read_insurance")]
    [Description("Reads all insurance records from the database. Returns firstname, lastname, email, contact number, insurance type, insurance start date, premium, and premium frequency for every record.")]
    public async Task> ReadInsuranceAsync()
    {
        try
        {
            return await _business.ReadInsuranceAsync();
        }
        catch (Exception ex)
        {
            throw new KernelException($"Failed to read Insurance table: {ex.Message}", ex);
        }
    }
}

Step 6 – Configure Program.cs

Program.cs wires together SQL Server, Dependency Injection, Semantic Kernel, OpenAI Chat Completion, automatic function calling, and the interactive console chat loop.


using InteractiveChatBot.Business;
using InteractiveChatBot.Data;
using InteractiveChatBot.Plugins;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.ChatCompletion;
using Microsoft.SemanticKernel.Connectors.OpenAI;

try
{
    var config = new ConfigurationBuilder()
        .SetBasePath(AppContext.BaseDirectory)
        .AddJsonFile("appsettings.json", optional: false, reloadOnChange: false)
        .Build();

    var connectionString = config.GetConnectionString("DefaultConnection")
        ?? throw new InvalidOperationException("Connection string 'DefaultConnection' not found.");

    var openAiModelId = config["OpenAI:ModelId"]
        ?? throw new InvalidOperationException("OpenAI:ModelId not found in appsettings.json.");

    var openAiApiKey = config["OpenAI:ApiKey"];
    if (string.IsNullOrWhiteSpace(openAiApiKey))
        throw new InvalidOperationException("OpenAI:ApiKey is missing in appsettings.json.");

    var openAiEndpoint = config["OpenAI:Endpoint"]
        ?? throw new InvalidOperationException("OpenAI:Endpoint not found in appsettings.json.");

    if (!Uri.TryCreate(openAiEndpoint, UriKind.Absolute, out var endpointUri))
        throw new InvalidOperationException($"OpenAI:Endpoint '{openAiEndpoint}' is not a valid absolute URI.");

    var services = new ServiceCollection();
    services.AddDbContext(options => options.UseSqlServer(connectionString));
    services.AddScoped();
    services.AddScoped();
    
services.AddSingleton(_ => new HttpClient
{
    BaseAddress = endpointUri,
    Timeout = TimeSpan.FromMinutes(3)
});

services.AddScoped(sp =>
{
    var builder = Kernel.CreateBuilder();

    builder.AddOpenAIChatCompletion(
        modelId: openAiModelId,
        apiKey: openAiApiKey,
        httpClient: sp.GetRequiredService());

    var kernel = builder.Build();

    kernel.Plugins.AddFromObject(
        sp.GetRequiredService(),
        "Insurance");

    return kernel;
});

await using var provider = services.BuildServiceProvider();
using var scope = provider.CreateScope();

var kernel = scope.ServiceProvider.GetRequiredService();
var chat = kernel.GetRequiredService();

var executionSettings = new OpenAIPromptExecutionSettings
{
    FunctionChoiceBehavior = FunctionChoiceBehavior.Auto()
};

var history = new ChatHistory(
    "You are an insurance assistant. When the user asks anything about policy holders, " +
    "insurance types, premiums, or start dates, call the Insurance.read_insurance function to fetch the data and answer based on the returned rows. Be concise.");
    
  using var cts = new CancellationTokenSource();

Console.CancelKeyPress += (_, e) =>
{
    e.Cancel = true;
    cts.Cancel();
};

Console.WriteLine("Insurance chatbot ready. Type 'exit' to quit (or Ctrl+C).");

while (!cts.IsCancellationRequested)
{
    Console.Write("\nYou: ");
    var input = Console.ReadLine();

    if (input is null) break;
    if (string.IsNullOrWhiteSpace(input)) continue;
    if (input.Trim().Equals("exit", StringComparison.OrdinalIgnoreCase))
        break;

    history.AddUserMessage(input);

    try
    {
        var reply = await chat.GetChatMessageContentAsync(
            history,
            executionSettings,
            kernel,
            cts.Token);

        Console.WriteLine($"\nBot: {reply.Content}");
        history.Add(reply);
    }
    catch (OperationCanceledException)
    {
        Console.WriteLine("\nRequest cancelled.");
        history.RemoveAt(history.Count - 1);
    }
    catch (HttpOperationException ex)
    {
        Console.Error.WriteLine(
            $"\nLLM endpoint error ({(int?)ex.StatusCode}): {ex.Message}");

        history.RemoveAt(history.Count - 1);
    }
}

catch (KernelException ex)
{
    Console.Error.WriteLine($"\nKernel error: {ex.Message}");
    history.RemoveAt(history.Count - 1);
}
catch (Exception ex)
{
    Console.Error.WriteLine($"\nUnexpected error: {ex.Message}");
    history.RemoveAt(history.Count - 1);
}

return 0;
}
catch (InvalidOperationException ex)
{
    Console.Error.WriteLine($"Configuration error: {ex.Message}");
    return 1;
}
catch (FileNotFoundException ex)
{
    Console.Error.WriteLine($"Missing file: {ex.Message}");
    return 1;
}
catch (Exception ex)
{
    Console.Error.WriteLine($"Fatal error: {ex.GetType().Name}: {ex.Message}");
    return 1;
}

Sample Questions

  • Show all insurance records.
  • Who has Medical insurance?
  • Which policies started in 2019?
  • Show Quarterly premium customers.
  • What is Rohit's premium amount?







Conclusion

You have successfully built an AI-powered chatbot that can answer natural language questions using live SQL Server data through .NET Semantic Kernel plugins. This architecture cleanly separates AI reasoning from business logic and can easily be extended for CRM, HR, Healthcare, Banking, and Inventory Management system.