You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

MAUI Blazor跨端OCR扫描器相机无法启动求助及可行代码需求

MAUI Blazor 跨平台OCR扫描器完整实现

一、平台权限配置

Android(Platform/Android/AndroidManifest.xml)

添加相机相关权限:

<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera" android:required="true" />
<uses-feature android:name="android.hardware.camera.autofocus" />

iOS(Platform/iOS/Info.plist)

添加相机权限说明:

<key>NSCameraUsageDescription</key>
<string>需要访问相机进行OCR文字识别</string>

二、Blazor 组件代码(Pages/OcrScanner.razor)

@page "/ocrscanner"
@inject IJSRuntime JSRuntime
@implements IAsyncDisposable

<h3>OCR扫描器</h3>

<div class="camera-container">
    <video id="videoFeed" width="320" height="240" style="transform: scaleX(-1);" />
    <canvas id="currentFrame" class="d-none" width="320" height="240" />
</div>

<div class="mt-3">
    <button class="btn btn-primary" @onclick="StartCamera">启动相机</button>
    <button class="btn btn-secondary ms-2" @onclick="StopCamera">停止相机</button>
    <button class="btn btn-success ms-2" @onclick="CaptureAndRecognize">识别文字</button>
</div>

<div class="mt-3">
    <h4>识别结果:</h4>
    <pre>@ocrResult</pre>
</div>

@code {
    private string ocrResult = "";
    private IJSObjectReference? _jsModule;
    private DotNetObjectReference<OcrScanner>? _dotNetReference;

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            _jsModule = await JSRuntime.InvokeAsync<IJSObjectReference>("import", "./js/ocr-handler.js");
            _dotNetReference = DotNetObjectReference.Create(this);
        }
    }

    private async Task StartCamera()
    {
        await _jsModule!.InvokeVoidAsync("startCamera", "videoFeed");
    }

    private async Task StopCamera()
    {
        await _jsModule!.InvokeVoidAsync("stopCamera", "videoFeed");
    }

    private async Task CaptureAndRecognize()
    {
        await _jsModule!.InvokeVoidAsync("captureFrameForOcr", "videoFeed", "currentFrame", _dotNetReference);
    }

    [JSInvokable]
    public async Task UpdateOcrResult(string recognizedText)
    {
        ocrResult = recognizedText;
        StateHasChanged();
    }

    public async ValueTask DisposeAsync()
    {
        if (_jsModule != null)
        {
            await _jsModule.DisposeAsync();
        }
        _dotNetReference?.Dispose();
    }
}

三、JavaScript 处理代码(wwwroot/js/ocr-handler.js)

先在wwwroot/index.html的<head>中引入Tesseract.js(前端OCR库):

<script src="https://cdn.jsdelivr.net/npm/tesseract.js@5.0.2/dist/tesseract.min.js"></script>

然后编写ocr-handler.js:

import * as Tesseract from 'tesseract.js';

// 初始化OCR工作线程,支持中英双语
const ocrWorker = Tesseract.createWorker({
    logger: m => console.log(m) // 可选,打印识别日志
});

async function initWorker() {
    await ocrWorker.load();
    await ocrWorker.loadLanguage('eng+chi_sim');
    await ocrWorker.initialize('eng+chi_sim');
}

// 启动相机
export async function startCamera(videoId) {
    try {
        const stream = await navigator.mediaDevices.getUserMedia({
            video: {
                facingMode: 'environment', // 指定使用后置摄像头
                width: { ideal: 1280 },
                height: { ideal: 720 }
            }
        });

        const videoElement = document.getElementById(videoId);
        videoElement.srcObject = stream;
        await videoElement.play();
    } catch (error) {
        console.error('相机启动失败:', error);
        alert('无法访问相机,请检查应用权限设置');
    }
}

// 停止相机
export function stopCamera(videoId) {
    const videoElement = document.getElementById(videoId);
    if (videoElement?.srcObject) {
        const stream = videoElement.srcObject;
        const tracks = stream.getTracks();
        tracks.forEach(track => track.stop());
        videoElement.srcObject = null;
    }
}

// 捕获帧并执行OCR
export async function captureFrameForOcr(videoId, canvasId, dotNetHelper) {
    // 确保OCR线程已初始化
    if (!ocrWorker.isLoaded()) {
        await initWorker();
    }

    const videoElement = document.getElementById(videoId);
    const canvasElement = document.getElementById(canvasId);
    const ctx = canvasElement.getContext('2d');

    // 将视频帧绘制到画布
    ctx.drawImage(videoElement, 0, 0, canvasElement.width, canvasElement.height);
    const imageData = ctx.getImageData(0, 0, canvasElement.width, canvasElement.height);

    // 执行OCR识别
    const recognitionResult = await ocrWorker.recognize(imageData);
    // 将结果传回C#端
    await dotNetHelper.invokeMethodAsync('UpdateOcrResult', recognitionResult.data.text);
}

// 页面卸载时销毁OCR线程
window.addEventListener('beforeunload', async () => {
    await ocrWorker.terminate();
});

四、核心注意事项

  • 权限优先级:必须先配置Android和iOS的平台权限,否则相机请求会被系统拒绝
  • 后置摄像头:通过facingMode: 'environment'强制使用后置摄像头,更适合文档扫描场景
  • 资源清理:实现IAsyncDisposable清理JS模块和DotNet引用,避免内存泄漏
  • 错误处理:添加相机启动失败的捕获逻辑,方便调试权限或设备兼容问题

内容的提问来源于stack exchange,提问作者neha

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.31 13:45:15