使用技巧

批量处理图片:高效OCR识别方案与最佳实践

学习如何批量处理多张图片进行OCR识别,包含循环调用、并发请求、错误处理和性能优化等完整方案。

阅读时间 13 分钟

为什么需要批量处理?

在实际业务场景中,您可能需要一次性处理大量图片:

  • 扫描归档的历史文档(几百到几千页)
  • 批量识别发票、收据进行财务录入
  • 处理用户上传的多张图片
  • 定时任务自动处理新增文件

单张图片逐个手动处理效率太低,需要自动化的批量处理方案。

方案一:顺序循环调用

最简单的方式是使用循环依次处理每张图片。这种方式实现简单,适合图片数量不多或对速度要求不高的场景。

JavaScript/Node.js 示例

async function batchOCR(images) {
  const results = [];
  
  for (const image of images) {
    const formData = new FormData();
    formData.append('file', image);

    try {
      const response = await fetch('https://api.easyocr.org/ocr', {
        method: 'POST',
        body: formData
      });
      
      const result = await response.json();
      results.push({ 
        filename: image.name, 
        success: true, 
        data: result 
      });
    } catch (error) {
      results.push({ 
        filename: image.name, 
        success: false, 
        error: error.message 
      });
    }
    
    // 添加延迟,避免请求过于频繁
    await new Promise(resolve => setTimeout(resolve, 200));
  }
  
  return results;
}

Python 示例

import requests
import time

def batch_ocr(image_paths):
    results = []
    
    for path in image_paths:
        try:
            with open(path, 'rb') as f:
                files = {'file': f}
                response = requests.post(
                    'https://api.easyocr.org/ocr',
                    files=files
                )
                result = response.json()
                results.append({
                    'filename': path,
                    'success': True,
                    'data': result
                })
        except Exception as e:
            results.append({
                'filename': path,
                'success': False,
                'error': str(e)
            })
        
        # 添加延迟
        time.sleep(0.2)
    
    return results

方案二:并发请求处理

如果需要更快的处理速度,可以使用并发请求同时处理多张图片。但需要注意控制并发数量,避免触发 API 限流。

JavaScript 并发示例

async function batchOCRConcurrent(images, concurrency = 3) {
  const results = [];
  
  // 将图片分成多个批次
  for (let i = 0; i < images.length; i += concurrency) {
    const batch = images.slice(i, i + concurrency);
    
    const promises = batch.map(async (image) => {
      const formData = new FormData();
      formData.append('file', image);
      
      try {
        const response = await fetch('https://api.easyocr.org/ocr', {
          method: 'POST',
          body: formData
        });
        return { 
          filename: image.name, 
          success: true, 
          data: await response.json() 
        };
      } catch (error) {
        return { 
          filename: image.name, 
          success: false, 
          error: error.message 
        };
      }
    });
    
    const batchResults = await Promise.all(promises);
    results.push(...batchResults);
    
    // 批次之间添加延迟
    if (i + concurrency < images.length) {
      await new Promise(resolve => setTimeout(resolve, 500));
    }
  }
  
  return results;
}

Python 异步并发示例

import asyncio
import aiohttp

async def ocr_single(session, image_path):
    try:
        with open(image_path, 'rb') as f:
            data = aiohttp.FormData()
            data.add_field('file', f, filename=image_path)
            
            async with session.post(
                'https://api.easyocr.org/ocr',
                data=data
            ) as response:
                result = await response.json()
                return {
                    'filename': image_path,
                    'success': True,
                    'data': result
                }
    except Exception as e:
        return {
            'filename': image_path,
            'success': False,
            'error': str(e)
        }

async def batch_ocr_async(image_paths, concurrency=3):
    semaphore = asyncio.Semaphore(concurrency)
    
    async def limited_ocr(session, path):
        async with semaphore:
            result = await ocr_single(session, path)
            await asyncio.sleep(0.2)  # 请求间隔
            return result
    
    async with aiohttp.ClientSession() as session:
        tasks = [limited_ocr(session, path) for path in image_paths]
        return await asyncio.gather(*tasks)

# 使用示例
# results = asyncio.run(batch_ocr_async(image_list))

错误处理与重试机制

批量处理时,部分请求可能因网络问题或临时错误而失败。实现重试机制可以提高整体成功率。

async function ocrWithRetry(image, maxRetries = 3) {
  for (let attempt = 1; attempt <= maxRetries; attempt++) {
    try {
      const formData = new FormData();
      formData.append('file', image);
      
      const response = await fetch('https://api.easyocr.org/ocr', {
        method: 'POST',
        body: formData
      });
      
      if (!response.ok) {
        throw new Error(`HTTP ${response.status}`);
      }
      
      return await response.json();
    } catch (error) {
      console.log(`尝试 ${attempt}/${maxRetries} 失败: ${error.message}`);
      
      if (attempt === maxRetries) {
        throw error;
      }
      
      // 指数退避:每次重试等待时间翻倍
      await new Promise(resolve => 
        setTimeout(resolve, 1000 * Math.pow(2, attempt - 1))
      );
    }
  }
}

进度跟踪与回调

处理大量图片时,提供进度反馈可以改善用户体验。

async function batchOCRWithProgress(images, onProgress) {
  const results = [];
  const total = images.length;
  
  for (let i = 0; i < images.length; i++) {
    const image = images[i];
    
    // 调用进度回调
    onProgress({
      current: i + 1,
      total: total,
      percentage: Math.round(((i + 1) / total) * 100),
      currentFile: image.name
    });
    
    // 处理图片...
    const result = await processImage(image);
    results.push(result);
  }
  
  return results;
}

// 使用示例
batchOCRWithProgress(images, (progress) => {
  console.log(`处理进度: ${progress.percentage}% (${progress.current}/${progress.total})`);
  console.log(`当前文件: ${progress.currentFile}`);
});

性能优化建议

1. 图片预处理

在上传前对图片进行压缩和优化,可以减少传输时间:

  • 将大图片压缩到合适尺寸(建议宽度不超过 2000px)
  • 使用 JPEG 格式并适当降低质量(80-90%)
  • 裁剪掉不需要识别的区域

2. 合理设置并发数

  • 并发数过低:处理速度慢
  • 并发数过高:可能触发限流或导致服务器压力
  • 建议值:3-5 个并发请求

3. 实现断点续传

对于大批量任务,保存处理进度,支持中断后继续:

// 保存进度到本地存储
function saveProgress(processedIds) {
  localStorage.setItem('ocr_progress', JSON.stringify(processedIds));
}

// 恢复进度
function loadProgress() {
  const saved = localStorage.getItem('ocr_progress');
  return saved ? JSON.parse(saved) : [];
}

4. 结果缓存

对于相同的图片,缓存识别结果避免重复请求。

注意事项

  • 遵守 API 使用规范:不要发送过于频繁的请求
  • 添加适当延迟:建议请求间隔 200ms 以上
  • 实现错误处理:网络请求可能失败,需要妥善处理
  • 监控处理状态:记录成功和失败的数量,便于排查问题
  • 考虑内存占用:大量图片同时加载可能导致内存不足

相关资源

了解更多 OCR 使用技巧:

这篇文章有帮助吗?

欢迎访问帮助中心

分享: