AI-Generation d'un texte Alt à puissance avec mostlylucid.llmlltText
Vous voulez obtenir un nice, texte alt descriptif pour les images sur vos sites ou jsut extraire du texte de ces images? mostlylucid.llmalttext utilise le modèle de langage visuel Microsoft's Florence-2 pour produire automatiquement un texte alt de qualité élevée - M SK3 qui fonctionne entièrement localement sur votre machine
Note: Je dois mettre à jour ce doc maintenant Nuget package est hors. Si vous regardez ici vous' je ferai fonctionner un bon site de démonstration que vous pouvez télécharger et utiliser.
Introduction
Le texte alt est important. Les lecteurs d’écran dépendent de lui, Les classements SEO l’influencent sur ce facteur , et c’est simplement la bonne chose à faire pour assurer l’accessibilitéM SK4 Mais il faut écrire un bon texte alt pour des centaines d'images.
Ce paquet résout ce problème en utilisant le modèle de langage visuel Microsoft' Florence-2 - qui fonctionne entièrement localement sur votre machine
Code de source: github.com /scottgal/mostlylucidM SK3nugetpackages
Le problème
Tous les <img> tag devrait avoir un texte alt significatif. Mais dans la pratique:
- La rédaction manuelle est acharnée - Des centaines d'images signifient des heures de travail
- Les API de l'AI coûtent cher - OpenAI Vision , Claude, etcM SK3 ajouter rapidement
- Préoccupations liées à la protection des renseignements personnels - vous ne souhaitez peut-être pas envoyer des images aux API externes
- Qualité incohérente - différentes personnes écrivent le texte alt différemment
Que se passerait-il si vous possédais générer automatiquement un texte alt de qualité élevée-qui fonctionne entièrement sur votre propre matériel?
Comment cela fonctionne
Le paquet utilise le modèle de Microsoft's Florence-2 via ONNX runtime . HereMSC3s the processing pipelineM SK4
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
Principales caractéristiques:
- Exécution locale - aucun appel à l’APIM SK1 aucun coût , aucune préoccupation en matière de protection des renseignements personnels
- Modèle ~800MB - télécharge une fois, cache pour toujours
- Types de tâche multiples - sous-titres succincts , descriptions détailléesM SK2 OCR
- Classification du contenu - sait s’il est une photo ' graphe
Démarrage rapide
Installation
dotnet add package Mostlylucid.LlmAltText
Services de registre
// Program.cs
builder.Services.AddAltTextGeneration();
Le premier essai télécharge le modèle Florence-2 (~800MB), puis vousMSC5vous êtes prêts à partirM SK6
Créer un texte Alt
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 });
}
}
Sources d'entrée multiples
Le service accepte les images de n'importe où - fichiers, URLs , fluxM SK3 ou tableaux de bytesMSC4
À partir du chemin de fichier
var altText = await _imageAnalysis.GenerateAltTextFromFileAsync("/images/photo.jpg");
De l'URL
var altText = await _imageAnalysis.GenerateAltTextFromUrlAsync(
"https://example.com/image.png");
De l'écoulement
using var stream = file.OpenReadStream();
var altText = await _imageAnalysis.GenerateAltTextAsync(stream);
De l'Arraye de Bytes
var bytes = await httpClient.GetByteArrayAsync(imageUrl);
var altText = await _imageAnalysis.GenerateAltTextAsync(bytes);
Types de tâche: Niveau de détail de contrôle
Florence-2 appuie trois modes de captioningM SK1 Choisissez selon vos besoins:
// 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."
Quand utiliser chacune:
| Type de tâche | Le meilleur pour |
|---|---|
CAPTION |
Diagrammes d'images miniaturesM SK1 images décoratives, conseils rapides sur les outils |
DETAILED_CAPTION |
Médias sociauxM SK1 accessibilité de base |
MORE_DETAILED_CAPTION |
pleine accessibilitéM SK1 lecteurs d'écran ( recommandée |
Extraction de texte OCR
Florence-2 peut aussi extraire du texte des images M SK1 utile pour les screenshots, documentsMST3 et diagrammesMSS4
// 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}");
Classification du type de contenu
Les images ne sont pas toutes les mêmes. Une photographie a besoin d'un texte alt descriptif; un document a besoin du contenu textuel de son objetM SK2 La fonction de classification vous aide à traiter chacune de ces images de façon appropriée :
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}");
Gestion des différents types de contenu
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;
}
Type de contenu Référence
| Type | Description MSC2 Exemple SMC3 | |
|---|---|---|
Photograph |
Photo réelleM SK1photos du monde | Personnes, paysagesMSC4 produits |
Document |
TexteM SK1 Contenu volumineux | PDFs, formulaires |
Screenshot |
Captures de logiciels | UIM SK2 sites Web, applications |
Chart |
Visualisations des données | |
Illustration |
Contenu dessiné | Travaux d'artM SK2 caricatures, icones |
Diagram |
Des dessins techniques | Des diagrammes d'écoulement |
Unknown |
Non classé | Cases à bord SSK2 |
L'Auto Alt Text TagHelper
Voici's où il devient intéressant. Le TagHelper génère automatiquement le texte alt pour n'importe quel <img> tag manquant un - au moment de la traduction.
Mise en place
// 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();
Inscrire le TagHelper dans _ViewImports.cshtml:
@addTagHelper *, Mostlylucid.LlmAltText
Comment cela fonctionne
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
Ce qui est traité
<!-- 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" />
Restrictions sur le domaine
Pour la sécurité, vous pouvez restreindre les domaines que le TagHelper fetchera de:
options.AllowedImageDomains = new List<string>
{
"mycdn.example.com",
"images.mysite.org",
"cdn.githubusercontent.com"
};
Cache des bases de données
Sans cache, chaque traduction de page régénérerait le texte alt. Que ' est lente et irrégulièreM SK3 Le cache de la base de données stocke les résultats saisis par l'image URLMSC4
SQLite (Development)
builder.Services.AddAltTextGeneration(options =>
{
options.EnableDatabase = true;
options.DbProvider = AltTextDbProvider.Sqlite;
options.SqliteDbPath = "./alttext.db";
options.CacheDurationMinutes = 60;
});
PostgreSQL (Production)
builder.Services.AddAltTextGeneration(options =>
{
options.EnableDatabase = true;
options.DbProvider = AltTextDbProvider.PostgreSql;
options.ConnectionString = Configuration.GetConnectionString("AltTextDb");
});
Référence de configuration
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;
});
Exemple mondiale: Traitement en lots réels
Voici la façon dont je l’utilise pour traiter les images lors de l’importation des articles sur le blog:
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";
}
}
Considérations relatives au rendement
Ce que l'on attend
| Metrique | Valeur typique |
|---|---|
| Démarrage | Moteur plus lente ~800 téléchargement du modèleMBM SK3 M |
| Charge de modèle | 1-3 secondes M |
| ParM SK1traitement d'images | 500-2000ms S |
| Utilisation de la mémoire | 2GBMSC3 recommandée S |
| espace de disques | MSC2MB pour les modèles SMC3 |
Conseils pour la production
// 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
}
Intégration de la télémétrie ouverte
Le paquet inclut le traçage intégré-:
builder.Services.AddOpenTelemetry()
.WithTracing(tracing =>
{
tracing.AddSource("Mostlylucid.LlmAltText");
});
Activités suivies:
llmalttext.generate_alt_textllmalttext.extract_textllmalttext.analyze_imagellmalttext.classify_content_type
Examens de santé
Ajouter un contrôle de santé pour surveiller l'état du modèle:
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");
Troubleshooting
Défaut de télécharger le modèle
Error: Failed to download model files
Solutions:
- Vérifier la connectivité à Internet
- Vérifier le pare-feu permet de télécharger Hugging Face
- Assurer la disponibilité de l'espace ~800MB sur le disque
- Vérifier les permissions d'écriture sur
ModelPath
Le service n'est pas prêt
_imageAnalysis.IsReady // Returns false
Solutions:
- attendre l'initialisation du modèle (1-3 secondes)
- Vérifier les registres des erreurs d'initialisation
- Vérifier la mémoire suffisante (2GB+)
La qualité médiocre du texte Alt
Solutions:
- Utilisation
MORE_DETAILED_CAPTION(default) - Assurez-vous que les images d'entrée sont claires
- Vérifier si l’image n’est pas trop petite ou floue
L'aide-étiquette ne fonctionne pas
Solutions:
- Vérifier
EnableTagHelper = true - Vérifier
@addTagHelperdans_ViewImports.cshtml - Utiliser des URL absolues (les voies relatives sont passées par erreur)
- Vérifier
AllowedImageDomainsConfiguration
Pratiques exemplaires en matière d'accessibilité
Le texte alt généré est un point de départ. Pour obtenir les meilleurs résultats:
- Examiner le résultat - L'AI n'est pas parfaiteM SK1t perfect, vérifier l'exactitude
- Gardez-le concis - 90-100 mots maximum
- Être descriptif - inclure les sujets, actionsM SK2 contexte
- Éviter la redondance - donM SK1 ne débute pas avec "Image de..."
- Examiner le but - le texte alt doit servir à l’image ' du rôle sur la page
- Utiliser alt vide pour la décoration - ensemble
alt=""pour des images purement décoratives - Inclure le texte visible - si l'image contient du texte, y inclure
Conclusion
Mostlylucid.LlmAltText permet une accessibilité -améliorée de l’AI à vos applications .NET sans les préoccupations liées au coût ou à la protection des renseignements personnels des API externes. Le TagHelper rend tout particulièrement facile <img> tags gain automatic alt text.
Le paquet est Unlicense (domain public), donc faites tout ce que vous voulez avec luiM SK2
Ressources
- NuGet: Mostlylucid.LlmAltText
- Source: github.com /scottgal/mostlylucidM SK3nugetpackages
- Émissions: Questions liées à GitHub
- Florence-2: Microsoft's Vision Language Model