Back to "AI-Powered Alt Text Generation with enindlucid.lltText"

This is a viewer only at the moment see the article on how this works.

To update the preview hit Ctrl-Alt-R (or ⌘-Alt-R on Mac) or Enter to refresh. The Save icon lets you save the markdown file to disk

This is a preview from the server running through my markdig pipeline

Accessibility AI ASP.NET Florence-2 Image Processing NuGet

AI-Powered Alt Text Generation with enindlucid.lltText

Monday, 24 November 2025

Haluatko saada mukavan, kuvailevan alt-tekstin sivuillesi tai poimia niistä tekstiä? mostlylucid.llmalttext Microsoftin Florence-2-vision language -mallilla luodaan laadukasta alt-tekstiä automaattisesti – se toimii täysin paikallisesti koneellasi, eikä API-avaimia tarvita.

Huomaa: Minun täytyy päivittää tämä doc nyt Nugettipaketti Jos katsot, se on poissa. täällä Saat hienon esittelysivuston, jonka voit ladata ja käyttää. Päivitän tämän yksityiskohdilla lähipäivinä.

NuGet Lisenssi: Luvaton

Johdanto

Alt-tekstillä on merkitystä. Näytönlukijat ovat siitä riippuvaisia, SEO:n rankingissa se otetaan huomioon, ja se on yksinkertaisesti oikea tapa saavuttaa saavutettavuus. Mutta hyvän alt-tekstin kirjoittaminen sadoille kuville? Siinä suurin osa meistä on puutteellisia.

Tämä paketti ratkaisee ongelman käyttämällä Microsoftin Florence-2-vision language -mallia, joka toimii täysin paikallisesti koneellasi, eikä API-avaimia tarvita.

Lähdekoodi: github.com/scottgal/mostlylucid.nuget-pakkaukset

Ongelma

Kaikki <img> Tagissa pitäisi olla mielekästä alt-tekstiä, mutta käytännössä:

  • Manuaalinen kirjoittaminen on tylsää - Sadat kuvat tarkoittavat tunteja työtä
  • AI-rajapinnat maksavat - OpenAI Vision, Claude jne. täsmää nopeasti
  • Yksityisyyttä koskevat kysymykset - et ehkä halua lähettää kuvia ulkoisiin API-puhelimiin
  • Epäjohdonmukainen laatu - eri ihmiset kirjoittavat alt-tekstiä eri tavalla

Entä jos pystyisit luomaan laadukasta alt-tekstiä automaattisesti, täysin omalla laitteistollasi?

Miten se toimii

Paketti käyttää Microsoftin Florence-2-mallia ONNX-ajon kautta. Tässä on käsittelyputki:

flowchart TB
    subgraph Input[Image Sources]
        A[File Path]
        B[URL]
        C[Stream]
        D[Byte Array]
    end

    subgraph Processing[Florence-2 Pipeline]
        E[Image Preprocessing]
        F[Vision Encoder]
        G[Language Decoder]
    end

    subgraph Output[Results]
        H[Alt Text]
        I[OCR Text]
        J[Content Type]
    end

    A --> E
    B --> E
    C --> E
    D --> E
    E --> F
    F --> G
    G --> H
    G --> I
    G --> J

    style A stroke:#10b981,stroke-width:2px
    style B stroke:#10b981,stroke-width:2px
    style C stroke:#10b981,stroke-width:2px
    style D stroke:#10b981,stroke-width:2px
    style F stroke:#6366f1,stroke-width:2px
    style G stroke:#6366f1,stroke-width:2px
    style H stroke:#ec4899,stroke-width:2px
    style I stroke:#ec4899,stroke-width:2px
    style J stroke:#ec4899,stroke-width:2px

Tärkeimmät ominaisuudet:

  • Paikallinen toteutus - ei API-puheluita, ei kustannuksia, ei yksityisyyden suojaa
  • ~800MB-malli - lataukset kerran, kätkössä ikuisesti
  • Useita tehtävätyyppejä - lyhyt kuvateksti, yksityiskohtaiset kuvaukset, OCR
  • Sisällön luokittelu - tietää, onko kyseessä kuva, kaavio, kuvakaappaus jne.

Pikakäynnistys

Asennus

dotnet add package Mostlylucid.LlmAltText

Rekisteripalvelut

// Program.cs
builder.Services.AddAltTextGeneration();

Ensimmäinen juoksu lataa Florence-2-mallin (~800MB), sitten olet valmis lähtöön.

Luo Alt-teksti

public class ImageController : ControllerBase
{
    private readonly IImageAnalysisService _imageAnalysis;

    public ImageController(IImageAnalysisService imageAnalysis)
    {
        _imageAnalysis = imageAnalysis;
    }

    [HttpPost("analyze")]
    public async Task<IActionResult> Analyze(IFormFile image)
    {
        using var stream = image.OpenReadStream();
        var altText = await _imageAnalysis.GenerateAltTextAsync(stream);

        return Ok(new { altText });
    }
}

Useita syöttölähteitä

Palvelu hyväksyy kuvia mistä tahansa - tiedostoista, URL-osoitteista, virroista tai tavusarjoista.

Tiedostopolulta

var altText = await _imageAnalysis.GenerateAltTextFromFileAsync("/images/photo.jpg");

URL-osoitteesta

var altText = await _imageAnalysis.GenerateAltTextFromUrlAsync(
    "https://example.com/image.png");

Virrasta

using var stream = file.OpenReadStream();
var altText = await _imageAnalysis.GenerateAltTextAsync(stream);

Byte Arraysta

var bytes = await httpClient.GetByteArrayAsync(imageUrl);
var altText = await _imageAnalysis.GenerateAltTextAsync(bytes);

Tehtävätyypit: Kontrolloiva yksityiskohtataso

Florence-2 tukee kolmea kuvatekstitilaa. Valitse tarpeidesi perusteella:

// Brief - "A dog sitting on grass"
var brief = await _imageAnalysis.GenerateAltTextAsync(stream, "CAPTION");

// Detailed - "A golden retriever sitting on green grass in a park"
stream.Position = 0;
var detailed = await _imageAnalysis.GenerateAltTextAsync(stream, "DETAILED_CAPTION");

// Most detailed (default) - Full accessibility description
stream.Position = 0;
var full = await _imageAnalysis.GenerateAltTextAsync(stream, "MORE_DETAILED_CAPTION");
// "A happy golden retriever with light fur sitting on lush green grass
//  in a sunny park, with trees visible in the background."

Milloin kutakin lääkettä käytetään:

Tehtävän tyyppi Parhaalle |-----------|----------| | CAPTION Peukalokynnet, koristekuvat, nopeat työkaluvihjeet | DETAILED_CAPTION Sosiaalinen media, peruspalvelut | MORE_DETAILED_CAPTION Täysi saavutettavuus, näytönlukijat (suositellut)

OCR-tekstin poisto

Florence-2 voi myös poimia tekstiä kuvista, joista on hyötyä kuvakaappauksissa, dokumenteissa ja kaavioissa.

// Extract text only
var extractedText = await _imageAnalysis.ExtractTextAsync(stream);

// Get both alt text and extracted text
var (altText, ocrText) = await _imageAnalysis.AnalyzeImageAsync(stream);

Console.WriteLine($"Alt: {altText}");
Console.WriteLine($"OCR: {ocrText}");

Sisällön tyyppiluokitus

Kaikki kuvat eivät ole samanlaisia. Valokuva tarvitsee kuvailevaa alt-tekstiä, asiakirja tarvitsee tekstisisältönsä. Luokitusominaisuus auttaa sinua käsittelemään kutakin asianmukaisesti:

var result = await _imageAnalysis.AnalyzeWithClassificationAsync(stream);

Console.WriteLine($"Type: {result.ContentType}");        // e.g., "Photograph"
Console.WriteLine($"Confidence: {result.ContentTypeConfidence:P0}"); // e.g., "87%"
Console.WriteLine($"Has Text: {result.HasSignificantText}");

Eri sisältötyyppien käsittely

var result = await _imageAnalysis.AnalyzeWithClassificationAsync(stream);

switch (result.ContentType)
{
    case ImageContentType.Document:
        // Documents - prioritize extracted text
        return result.ExtractedText;

    case ImageContentType.Screenshot:
        // Screenshots - combine description with UI text
        return result.HasSignificantText
            ? $"{result.AltText}. Text visible: {result.ExtractedText}"
            : result.AltText;

    case ImageContentType.Chart:
        // Charts - describe the visualization plus data
        return $"{result.AltText}. Data: {result.ExtractedText}";

    case ImageContentType.Photograph:
    default:
        // Photos - just the description
        return result.AltText;
}

Sisällön tyypin viite

Tyyppi Kuvaus Esimerkki |------|-------------|---------| | Photograph Reaalimaailman kuvat Ihmiset, maisemat, tuotteet | Document Tekstipainotteinen sisältö PDF, lomakkeet, artikkelit | Screenshot Ohjelmisto vangitsee UI:n, verkkosivut, sovellukset | Chart Datan visualisoinnit Graafeja, piirakkataulukoita, taulukoita | Illustration Piirretty sisältö Taideteos, sarjakuvat, kuvakkeet | Diagram Tekniset piirustukset vuokaaviot, UML, kaaviot | Unknown Luokittelemattomat asiat

Automaattinen Alt Text Tag Helper

TagHelper tuottaa automaattisesti alt-tekstiä kaikille <img> lappu puuttuu yhdestä - renderointihetkellä.

Asetukset

// Program.cs
builder.Services.AddAltTextGeneration(options =>
{
    options.EnableTagHelper = true;
    options.EnableDatabase = true;  // Cache results
    options.DbProvider = AltTextDbProvider.Sqlite;
    options.SqliteDbPath = "./alttext.db";
});

var app = builder.Build();
await app.Services.MigrateAltTextDatabaseAsync();

Rekisteröi TagHelper sisään _ViewImports.cshtml:

@addTagHelper *, Mostlylucid.LlmAltText

Miten se toimii

flowchart LR
    subgraph Razor[Razor View Rendering]
        A[img tag found]
        B{Has alt attribute?}
        C[Skip - use existing]
        D{In cache?}
        E[Return cached]
        F[Fetch image]
        G[Generate alt text]
        H[Cache result]
        I[Render with alt]
    end

    A --> B
    B -->|Yes| C
    B -->|No| D
    D -->|Yes| E
    D -->|No| F
    F --> G
    G --> H
    H --> I
    E --> I

    style A stroke:#10b981,stroke-width:2px
    style B stroke:#6366f1,stroke-width:2px
    style G stroke:#ec4899,stroke-width:2px
    style I stroke:#8b5cf6,stroke-width:2px

Mitä käsitellään

<!-- NO ALT - Will be processed -->
<img src="https://example.com/photo.jpg" />

<!-- HAS ALT - Skipped (respects your text) -->
<img src="https://example.com/photo.jpg" alt="My custom description" />

<!-- EMPTY ALT - Skipped (decorative image per a11y standards) -->
<img src="https://example.com/decorative.jpg" alt="" />

<!-- EXPLICIT SKIP - Skipped -->
<img src="https://example.com/photo.jpg" data-skip-alt="true" />

<!-- DATA URI - Skipped (can't fetch) -->
<img src="data:image/png;base64,..." />

<!-- RELATIVE PATH - Skipped (needs absolute URL) -->
<img src="/images/photo.jpg" />

Verkkoalueen rajoitukset

Turvallisuuden vuoksi voit rajoittaa, mitä verkkotunnuksia TagHelper noutaa:

options.AllowedImageDomains = new List<string>
{
    "mycdn.example.com",
    "images.mysite.org",
    "cdn.githubusercontent.com"
};

Tietokannan välimuisti

Ilman välilyöntejä jokainen sivu renderöi Alt-tekstin uudelleen. Se on hidasta ja tuhlailevaa. Tietokannan välimuistivarastojen tulokset keyed by image URL.

SQLite (Kehitys)

builder.Services.AddAltTextGeneration(options =>
{
    options.EnableDatabase = true;
    options.DbProvider = AltTextDbProvider.Sqlite;
    options.SqliteDbPath = "./alttext.db";
    options.CacheDurationMinutes = 60;
});

PostgreSQL (tuote)

builder.Services.AddAltTextGeneration(options =>
{
    options.EnableDatabase = true;
    options.DbProvider = AltTextDbProvider.PostgreSql;
    options.ConnectionString = Configuration.GetConnectionString("AltTextDb");
});

Konfiguraation viite

builder.Services.AddAltTextGeneration(options =>
{
    // Model location (~800MB downloaded here)
    options.ModelPath = "./models";

    // Default task type for alt text generation
    options.DefaultTaskType = "MORE_DETAILED_CAPTION";

    // Maximum word count for alt text
    options.MaxWords = 90;

    // Enable detailed logging
    options.EnableDiagnosticLogging = true;

    // TagHelper settings
    options.EnableTagHelper = true;
    options.EnableDatabase = true;
    options.AutoMigrateDatabase = true;

    // Database provider
    options.DbProvider = AltTextDbProvider.Sqlite;
    options.SqliteDbPath = "alttext.db";
    // or
    options.DbProvider = AltTextDbProvider.PostgreSql;
    options.ConnectionString = "Host=localhost;Database=alttext;...";

    // Security
    options.AllowedImageDomains = new List<string> { "cdn.example.com" };
    options.SkipSrcPrefixes = new List<string> { "data:", "blob:" };

    // Caching
    options.CacheDurationMinutes = 60;
});

Reaalimaailman esimerkki: Eränkäsittely

Näin käsittelen kuvia tuodessani blogikirjoituksia:

public class ImageProcessor
{
    private readonly IImageAnalysisService _imageAnalysis;
    private readonly ILogger<ImageProcessor> _logger;

    public ImageProcessor(
        IImageAnalysisService imageAnalysis,
        ILogger<ImageProcessor> logger)
    {
        _imageAnalysis = imageAnalysis;
        _logger = logger;
    }

    public async Task ProcessMarkdownImagesAsync(string markdownPath)
    {
        var imageDir = Path.Combine(Path.GetDirectoryName(markdownPath)!, "images");
        if (!Directory.Exists(imageDir)) return;

        var images = Directory.GetFiles(imageDir, "*.*")
            .Where(f => IsImageFile(f));

        foreach (var imagePath in images)
        {
            try
            {
                var result = await _imageAnalysis
                    .AnalyzeWithClassificationFromFileAsync(imagePath);

                _logger.LogInformation(
                    "Processed {File}: {Type} ({Confidence:P0})",
                    Path.GetFileName(imagePath),
                    result.ContentType,
                    result.ContentTypeConfidence);

                // Store alt text for later use
                await SaveAltTextAsync(imagePath, result.AltText);
            }
            catch (Exception ex)
            {
                _logger.LogWarning(ex, "Failed to process {File}", imagePath);
            }
        }
    }

    private static bool IsImageFile(string path)
    {
        var ext = Path.GetExtension(path).ToLowerInvariant();
        return ext is ".jpg" or ".jpeg" or ".png" or ".gif" or ".webp" or ".bmp";
    }
}

Suorituskykyä koskevia huomioita

Mitä odottaa

Tyypillinen arvo |--------|--------------| Ensimmäinen kerta Hitaampi (noin 800MB mallin lataus) Mallikuorma 1-3 sekuntia Kuvankäsittely 500–2000 ms 2GB+:n käyttösuositus Levytilaa ~800MB malleille

Vinkkejä tuotantoon

// 1. Register as Singleton (model load is expensive)
builder.Services.AddAltTextGeneration(); // Already singleton internally

// 2. Check readiness before processing
if (!_imageAnalysis.IsReady)
{
    return StatusCode(503, "AI model still initializing");
}

// 3. Use cancellation tokens for timeouts
var cts = new CancellationTokenSource(TimeSpan.FromSeconds(30));
var altText = await _imageAnalysis.GenerateAltTextFromUrlAsync(url, cts.Token);

// 4. Process in batches, not parallel (memory constraints)
foreach (var image in images)
{
    await ProcessImageAsync(image); // Sequential is safer
}

OpenTelemetria Integrointi

Paketti sisältää sisäänrakennetun jäljityksen:

builder.Services.AddOpenTelemetry()
    .WithTracing(tracing =>
    {
        tracing.AddSource("Mostlylucid.LlmAltText");
    });

Jäljitetyt toiminnot:

  • llmalttext.generate_alt_text
  • llmalttext.extract_text
  • llmalttext.analyze_image
  • llmalttext.classify_content_type

Terveystarkastukset

Lisää terveystarkastus mallin tilan seuraamiseksi:

public class AltTextHealthCheck : IHealthCheck
{
    private readonly IImageAnalysisService _service;

    public AltTextHealthCheck(IImageAnalysisService service)
        => _service = service;

    public Task<HealthCheckResult> CheckHealthAsync(
        HealthCheckContext context,
        CancellationToken cancellationToken = default)
    {
        return Task.FromResult(_service.IsReady
            ? HealthCheckResult.Healthy("Florence-2 model ready")
            : HealthCheckResult.Unhealthy("Model not initialized"));
    }
}

// Registration
builder.Services.AddHealthChecks()
    .AddCheck<AltTextHealthCheck>("alttext");

Vianetsintä

Mallin lataus epäonnistui

Error: Failed to download model files

Ratkaisut:

  • Tarkista internetyhteydet
  • Varmenna palomuuri mahdollistaa Höyrykasvojen lataamisen
  • Varmista, että ~800MB levytilaa on saatavilla
  • Tarkista kirjoitusoikeudet ModelPath

Palvelus ei ole valmis

_imageAnalysis.IsReady // Returns false

Ratkaisut:

  • Odota mallin alustusta (1-3 sekuntia)
  • Tarkista lokit alustusvirheiden varalta
  • Varmista riittävä muisti (2GB+)

Huonolaatuista Alt-tekstiä

Ratkaisut:

  • Käyttö MORE_DETAILED_CAPTION (oletus)
  • Varmista, että syötekuvat ovat selkeitä
  • Tarkistuskuva ei ole liian pieni tai sumea

TagHelper ei toimi

Ratkaisut:

  • Varmista EnableTagHelper = true
  • Tarkista @addTagHelper in _ViewImports.cshtml
  • Käytä absoluuttisia URL-osoitteita (suhteelliset polut ohitetaan)
  • Tarkista AllowedImageDomains kokoonpano

Parhaita käytäntöjä saavutettavuudesta

Luotu alt-teksti on lähtökohta. Parhaille tuloksille:

  1. Tarkista tuotos - Tekoäly ei ole täydellinen, varmista tarkkuus
  2. Pidä se ytimekkäänä - 90-100 sanaa enintään
  3. Kuvaile - sisältää aiheita, toimia, kontekstia
  4. Vältä irtisanomista - Älä aloita "Kuva..."
  5. Harkitse tarkoitusta - Alt-tekstin pitäisi palvella kuvan roolia sivulla
  6. Käytä tyhjää alttia koristeluun - asetettu alt="" puhtaasti koristemaisille kuville
  7. Sisällytä näkyvä teksti - Jos kuva sisältää tekstiä, sisällytä se

Päätelmät

Mostlylucid.LlmAltText Tuo tekoälykäyttöisen pääsyn .NET-sovelluksiisi ilman ulkoisten sovellusliittymien kustannuksia tai yksityisyyttä. TagHelper tekee sen erityisen helpoksi - ota se ja <img> tunnisteet saavat automaattisen alt-tekstin.

Paketti on Unlicense (julkinen valta-alue), joten tee sille mitä haluat.

Resurssit

logo

© 2026 Scott Galloway — Unlicense — All content and source code on this site is free to use, copy, modify, and sell.