Back to "以 mostluccid.llmllt 文本生成的 AI 授权 Alt 文本"

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

以 mostluccid.llmllt 文本生成的 AI 授权 Alt 文本

Monday, 24 November 2025

想要在您的网站或 Jsut 从中提取图像的精美、描述性备选案文文本吗 ? mostlylucid.llmalttext 使用微软的佛罗伦萨-2视觉语言模型自动生成高质量的平价文本 - 完全在本地运行在您的机器上, 不需要 API 键 。

注:我现在需要更新这个文件 核金包包 向外是 。如果您查看 在这里 你将罚款一个可以下载和使用的缩写网站。 我将在未来几天更新这个网站的细节。

N Nuget 元数 许可证:无许可证

一. 导言 导言 导言 导言 导言 导言 一,导言 导言 导言 导言 导言 导言

Alt 文本很重要。 屏幕阅读器依赖它, SEO 排名将它考虑在内, 而这仅仅是方便访问的正确方法。 但是要为成百上千的图像写好的 alt 文本吗 ? 这就是我们大多数人的缺点所在 。

这个软件包用微软的佛罗伦萨-2视觉语言模型解决了这个问题, 完全在本地运行, 不需要 API 键 。

源代码 : Github.com/scottgal/ mostlylylucid. 核植物包装

问题

每个 <img> 标签应该有有意义的 alt 文本。 但实际上 :

  • 手工写作太烦人了 - 数以百计的图像意味着工作时间
  • AIAI AIP AI AI AI AI AI AI AI AI AI AI AI AI AI AI AI AI AI AI AI AI AIIT AITI AIIT AITI AITI AITI AITI AITI AITI AITI AITI AITI AITI AITI AIIT AIIT AITI AITI AIIT AITI AIIT AIIT AITI AITI AITI AITI AIIT AI AIIT AI AI AI AI AI AI AI AI AI AI AI AI AI AI AI AI AI AI AI AI AI AI AI AI AI AI AI AI AI AI AI AI AI AI AI AI AI AI AI AI AI AI AI AI AI AI AI AI AI AI AI AI AI AI AI - OpenAI View,Claude,等等,加起来很快
  • 隐私关注 - 您可能不想将图像发送到外部 API
  • 质量不一致 - 不同的人写备选案文文字的方式不同

如果你能自动生成高质量的平价文本 完全靠你自己的硬件运行呢?

如何运作

软件包使用微软佛罗伦萨2号模型 通过 ONNX 运行时间。

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

关键特征:

  • 当地执行 - 没有AIP电话,没有费用,没有隐私问题
  • ~800MB 模型 - 下载一次,永远隐藏
  • 多重任务类型 - 简要说明、详细说明、说明
  • 内容分类分类 - 知道它是否是一个照片,图表, 截图,等等。

快速启动

安装安装

dotnet add package Mostlylucid.LlmAltText

登记册服务

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

这就对了。第一次运行下载佛罗伦萨2号模型 (~ 800MB), 然后你就准备出发了。

生成 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 });
    }
}

多种投入来源

服务接受来自任何地方的图像 - 文件、 URL、 流或字节数组 。

从文件路径

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

从 URL 调自 URL

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

来自溪流

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

来自字节数组

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

任务类型: 控制详细级别

Florence-2 支持三个标题模式。 根据您的需要选择 :

// 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."

何时使用 :

  • 任务类型 * * 最适合 * * 任务类型 * * 任务类型 * * 任务类型 * 任务类型 * 任务类型 * * 任务类型 * 任务类型 * 任务类型 * 任务类型 * 任务类型 * 任务类型 * 任务类型 * 任务类型 * 任务类型 * 最佳类型 * 任务类型 * 任务类型 * 任务类型 * 任务类型 * |-----------|----------| | CAPTION 缩略图、装饰图象、快速提示 | DETAILED_CAPTION 社交媒体,基本无障碍 | MORE_DETAILED_CAPTION 完全无障碍,屏幕阅读器(建议)

OCR 文字采掘

Florence-2还可以从图像中提取文字,用于截图、文件和图表。

// 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}");

分类 分类 分类 分类 分类 分类 分类 分类 分类 分类 分类 分类 分类 分类 分类 分类 分类 分类 分类 分类 分类 分类 分类 分类 分类 分类 分类 分类 分类 分类 分类 分类 分类 分类 分类 分类 分类 分类 分类 分类 分类 分类 分类 分类 分类 分类 分类 分类 分类 分类 分类 分类

并非所有图像都是相同的。 照片需要描述性备选案文文本; 文件需要其文本内容。 分类功能有助于您正确处理每个图像 :

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}");

处理不同内容类型

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;
}

内容类型 参考

|------|-------------|---------| | Photograph 真实世界照片 人民 风景 产品 | Document PDFs、表格、条款 | Screenshot 軟體捕捉 & UI, 網站, 應用程式 | Chart * 数据可视化 * 图表、饼图、表 * | Illustration 绘画内容 艺术作品 漫画 图标 | Diagram * 技术图纸 * * 流程图画 * * UML, 图表 * * * | Unknown

自动 Alt 文本标记辅助工具

有趣的是,标签帮助器自动为任何 <img> 缺少一个标记 - 在转换时间 。

设置设置设置设置设置设置设置

// 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();

注册标签帮助者 _ViewImports.cshtml:

@addTagHelper *, Mostlylucid.LlmAltText

如何运作

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

正在处理的东西

<!-- 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" />

域域限制

为了安全起见, 您可以限制 TagHelfer 将从哪个区域获取 :

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

数据库缓存

没有缓存, 每个页面翻转都会重新生成 alt 文本 。 这是缓慢和浪费的 。 数据库缓存由图像 URL 键制的结果 。

SQLite(开发)

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

PostgreSQL(生产)

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

配置引用

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;
});

真实世界实例:批处理

以下是我在进口博客文章时如何使用它处理图像:

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";
    }
}

绩效考量

要期待什么

  • 典型价值 * * Metric * * 典型价值 * * * * 典型价值 * * * * Metric * * 典型价值 * * * * 典型价值 * * * 典型价值 * * * 典型价值 * * * Metric * Metric * * medicle simonal valu * * * * 典型价值 * * * * 典型价值 * * * * * * 典型价值 * * * * * * 典型价值 * * * * * * |--------|--------------| 第一次运行 减速 (~ 800MB 模式下载) 型号负载 *% 1-3秒 *
  • 每张图像处理 * * 500 - 2000 ms *
  • 记忆使用 * * 2GB+建议 * 磁盘空间~~800MB 模型

生产小费

// 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
}

开放遥测集成

包包包括内在追踪:

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

追踪活动:

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

健康检查

添加健康检查以监测模型状态:

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");

排除故障

模型下载失败

Error: Failed to download model files

解决办法:

  • 检查互联网连接
  • 校验防火墙允许 Huggging Face 下载
  • 确保800MB磁盘空间可用
  • 检查在 ModelPath

未就绪服务

_imageAnalysis.IsReady // Returns false

解决办法:

  • 等待模型初始化(1-3秒)
  • 检查初始化错误的日志
  • 核查足够的内存(2GB+)

低质量平文本

解决办法:

  • 使用使用 MORE_DETAILED_CAPTION (违约)
  • 确保输入图像清晰
  • 检查图像不会太小或太模糊

标签帮助器不工作

解决办法:

  • 核查核查 EnableTagHelper = true
  • 支票支票支票支票 @addTagHelper_ViewImports.cshtml
  • 使用绝对 URL( 对应路径被跳过)
  • 支票支票支票支票 AllowedImageDomains 配置配置配置

无障碍获取最佳做法

生成的alt 文本是一个起点。 最好的结果 :

  1. 审查产出 - 人工智能不完美,核查准确性
  2. 保持简洁 - 最多90-100字
  3. 叙述性说明 - 包括主题、行动、背景
  4. 避免冗余 - 不要从"形象"开始...
  5. 考虑目的 - 备选文本应该为页面上的图像角色服务
  6. 使用空 alt 用于装饰 - 成套成套 alt="" 纯装饰图像
  7. 包含可见文字 - 如果图像包含文字,包括文字

结论 结论 结论 结论 结论

Mostlylucid.LlmAltText 您的 .NET 应用程序无需外部 API 的成本或隐私考虑,而由 AI 驱动的 AI 无障碍 进入您的 .NET 应用程序。 TagHelfer 使得它特别容易 — — 仅能帮助您和您 <img> 标签获得自动 alt 文本。

软件包是 Unlicense( 公共域) , 所以可以随心所欲地使用它 。

资源资源资源 资源资源资源 资源资源 资源资源

logo

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