0

0

Tesseract.js 多列图像文本识别优化:利用页面分段模式解决文本混淆

DDD

DDD

发布时间:2025-11-21 15:27:25

|

300人浏览过

|

来源于php中文网

原创

Tesseract.js 多列图像文本识别优化:利用页面分段模式解决文本混淆

本文旨在解决使用 tesseract.js 进行多列图像文本识别时,文本内容混淆的问题。通过调整 tesseract.js 的页面分段模式(psm),特别是采用 `psm_auto_osd` 模式,可以有效改善 ocr 结果的准确性和列分离效果。教程将详细介绍如何配置 psm 参数,并提供 react.js 示例代码,帮助开发者优化多列文档的文本提取流程。

在使用 Tesseract.js 进行光学字符识别(OCR)时,处理包含多列文本的图像是一个常见的挑战。默认情况下,Tesseract 引擎可能将整个图像视为一个单一的文本块,导致不同列的文本内容混淆,输出结果难以阅读和处理。解决这一问题的关键在于理解并正确配置 Tesseract 的页面分段模式(Page Segmentation Mode, 简称 PSM)。

理解页面分段模式 (PSM)

页面分段模式(PSM)是 Tesseract OCR 引擎的一个核心配置参数,它指导引擎如何分析图像的布局,将其分解为文本块、行和单词。选择合适的 PSM 对于获得高质量的 OCR 结果至关重要,尤其是在处理复杂布局的文档时。

Tesseract 提供了多种 PSM 模式,每种模式都适用于不同的文档结构:

  • PSM_SINGLE_BLOCK (PSM=3): 这是 Tesseract.js 的默认模式。它假设图像包含一个统一的文本块。对于单列、排版简单的文档,此模式通常效果良好。然而,当图像包含多列文本时,它会将所有列的文本视为一个连续的块,导致文本混淆。
  • PSM_AUTO_OSD (PSM=1): 此模式会自动检测页面的方向和脚本(OSD),然后尝试识别图像中的文本块。它通常能更好地处理多列和复杂布局,因为它会尝试将页面分解为逻辑上的文本区域,从而有助于区分不同的列。
  • PSM_AUTO (PSM=0): 自动页面分割,但不进行 OSD。
  • PSM_SINGLE_COLUMN (PSM=4): 假设图像是单列文本。
  • PSM_SPARSE_TEXT (PSM=11): 寻找稀疏文本,适用于没有明确布局的文本区域。

对于多列图像,PSM_AUTO_OSD (PSM=1) 通常是最佳的起始点,因为它能够智能地识别并分离不同的文本区域。

在 Tesseract.js 中配置 PSM

在 Tesseract.js 中,可以通过 recognize 方法的第三个参数(一个 options 对象)来设置 PSM。关键的选项是 tessedit_pageseg_mode,其值对应于上述的 PSM 数字。

Imagine By Magic Studio
Imagine By Magic Studio

AI图片生成器,用文字制作图片

下载

要解决多列文本混淆问题,我们需要将 tessedit_pageseg_mode 设置为 1 (即 PSM_AUTO_OSD)。

以下是修改后的 React.js 组件代码片段,展示了如何在 handleSubmit 函数中应用此配置:

import React, { useState, useEffect } from "react";
import Tesseract from "tesseract.js";
import ClipboardJS from "clipboard";
import Select from "react-select";

const languageOptions = [
  // ... (语言选项保持不变)
  { value: "eng", label: "English" },
  { value: "chi_sim", label: "Chinese - Simplified" },
  // ... 其他语言
];

const ImagesToText = () => {
  const [isLoading, setIsLoading] = useState(false);
  const [images, setImages] = useState([]);
  const [texts, setTexts] = useState([]);
  const [progress, setProgress] = useState(0);
  const [currentImageIndex, setCurrentImageIndex] = useState(0);
  const [errorMessage, setErrorMessage] = useState("");
  const [errorLanguagesMessage, setErrorLanguagesMessage] = useState("");
  const [selectedLanguages, setSelectedLanguages] = useState([]);

  const handleImageUpload = (e) => {
    const selectedImages = Array.from(e.target.files);
    setImages(selectedImages);
    setErrorMessage("");
  };

  const handleCopyText = () => {
    const textWithSoftLineBreaks = texts.join("\n");
    navigator.clipboard.writeText(textWithSoftLineBreaks);
  };

  const handleDownloadText = () => {
    const element = document.createElement("a");
    const textBlob = new Blob([texts.join("\n")], { type: "text/plain" });
    element.href = URL.createObjectURL(textBlob);
    element.download = "converted_text.txt";
    document.body.appendChild(element);
    element.click();
    document.body.removeChild(element);
  };

  useEffect(() => {
    const clipboard = new ClipboardJS(".copy-button");

    clipboard.on("success", (e) => {
      e.clearSelection();
    });

    return () => {
      clipboard.destroy();
    };
  }, [texts]);

  const handleReset = () => {
    setIsLoading(false);
    setImages([]);
    setTexts([]);
    setProgress(0);
    setCurrentImageIndex(0);
    setErrorMessage("");
    setErrorLanguagesMessage("");

    window.location.reload();
  };

  const handleSubmit = async () => {
    if (images.length === 0) {
      setErrorMessage("Select an image to convert.");
      return;
    }

    if (selectedLanguages.length === 0) {
      setErrorLanguagesMessage("Select any language.");
      return;
    }

    setIsLoading(true);
    setProgress(0);
    setTexts([]);
    setCurrentImageIndex(0);
    setErrorMessage("");
    setErrorLanguagesMessage("");

    const totalImages = images.length;
    let processedImages = 0;

    if (Array.isArray(images)) {
      for (const [index, image] of images?.entries()) {
        setCurrentImageIndex(index + 1);

        try {
          // 核心修改:在 recognize 方法中添加 options 对象,设置 tessedit_pageseg_mode
          const result = await Tesseract.recognize(
            image,
            selectedLanguages.map((lang) => lang.value).join("+"),
            {
              // 启用自动页面方向和脚本检测,并尝试识别文本块
              // PSM_AUTO_OSD 对应的数值是 1
              tessedit_pageseg_mode: 1
            }
          );
          // Tesseract 识别结果中的文本通常会根据其内部逻辑进行分段。
          // 对于多列文档,PSM_AUTO_OSD 模式会尝试先识别列,然后按列顺序输出文本。
          // 这里的文本后处理逻辑可以根据实际需求调整,例如:
          // 如果 PSM_AUTO_OSD 成功将列分开,result.data.text 可能会按列顺序排列。
          // 原始代码中的分段逻辑:
          const paragraphs = result.data.text.split("\n\n");
          const formattedParagraphs = paragraphs.map((paragraph) => {
            const sentences = paragraph.split(/[.|?]\s/);
            return sentences.join(" ");
          });
          setTexts((prevTexts) => [...prevTexts, ...formattedParagraphs]);
        } catch (err) {
          console.error(err);
          // 发生错误时,清除文本并立即停止转换过程
          setTexts([]);
          setProgress(0);
          setIsLoading(false);
          return;
        } finally {
          processedImages++;
          const currentProgress = (processedImages / totalImages) * 100;
          setProgress(currentProgress);
        }
      }
    } else {
      console.error("Images is not an array.");
    }

    setIsLoading(false);
  };

  return (
    <div className="container" style={{ height: "97vh" }}>
      <div className="row h-100 mt-3">
        <div className="col-md-3 left-bar sticky-top border 1 ms-2">
          <h1 className="center py-3 mc-5 underline">Images to text (ocr)</h1>
          <input
            type="file"
            onChange={handleImageUpload}
            className="form-control mt-5 mb-2"
            multiple
            accept="image/*"
          />
          {errorMessage && <p className="text-danger">{errorMessage}</p>}
          <Select
            isMulti
            options={languageOptions}
            value={selectedLanguages}
            onChange={setSelectedLanguages}
            placeholder="Select languages..."
          />

          {errorLanguagesMessage && (
            <p className="text-danger">{errorLanguagesMessage}</p>
          )}

          <input
            type="button"
            onClick={handleSubmit}
            className="btn btn-outline-success mt-3"
            value="Start Convert"
          />
          {texts.length > 0 && (
            <button
              className="btn btn-primary mt-3 ms-1"
              onClick={handleDownloadText}
            >
              Download Text
            </button>
          )}
          <div className="mt-1">
            <button className=" btn ml-2 btn-danger" onClick={handleReset}>
              Reset
            </button>

            <button
              className="mt-3 btn btn-secondary d-inline ms-1 "
              onClick={handleCopyText}
            >
              Copy Text
            </button>
          </div>
        </div>
        <div className="col-md-8 right-bar border 1 ms-2">
          <h4 className="mt-5 text-center">Select an Image to convert (ocr)</h4>
          {isLoading && (
            <div className="text-center">
              <div className="text-center">
                <progress
                  className="custom-progress-bar"
                  value={progress}
                  max="100"
                ></progress>
                <p className="text-center py-0 my-0">
                  Converting...: {progress.toFixed(0)}% ({currentImageIndex} of{" "}
                  {images.length})
                </p>
              </div>
            </div>
          )}
          {!isLoading && texts.length > 0 && (
            <div>
              <div className="form-control box-p w-100 mt-5 m-none">
                {texts.map((paragraph, index) => (
                  <p key={index}>{paragraph}</p>
                ))}
              </div>
            </div>
          )}
        </div>
      </div>
    </div>
  );
};

export default ImagesToText;

注意事项与最佳实践

  1. 图像质量是基础: 无论选择何种 PSM,OCR 效果都与输入图像的质量密切相关。高分辨率、清晰、对比度适中、无倾斜、无噪声的图像将显著提高识别准确率。
  2. 图像预处理: 对于扫描件或质量不佳的图像,在进行 OCR 之前进行预处理非常重要。常见的预处理技术包括:
    • 灰度化与二值化: 将彩色图像转换为灰度图,再转换为黑白二值图,有助于突出文本。
    • 去噪: 移除图像中的杂点和干扰。
    • 倾斜校正(Deskewing): 自动检测并校正图像的倾斜角度,使文本行水平对齐。
    • 裁剪(Cropping): 如果图像中包含大量非文本区域,可以预先裁剪掉,只保留需要识别的部分。
  3. 选择正确的语言包: 确保 selectedLanguages 数组中包含文档内容的正确语言。对于多语言文档,可以组合使用语言包,例如 eng+chi_sim。
  4. 实验不同的 PSM 模式: 尽管 PSM_AUTO_OSD (PSM=1) 对于多列文档通常效果最好,但并非万能。对于某些特殊或极具挑战性的布局,可能需要尝试其他 PSM 模式,如 PSM_AUTO (PSM=0) 或 PSM_SINGLE_COLUMN (PSM=4),以找到最适合特定文档的模式。
  5. 后处理: 即使 OCR 结果有所改善,也可能需要对提取的文本进行进一步的清洗和格式化。例如,去除多余的空格、合并因识别错误而断裂的单词、或根据文档结构重新组织文本。

总结

通过在 Tesseract.js 中正确配置页面分段模式,特别是使用 PSM_AUTO_OSD (PSM=1),可以有效解决多列图像文本识别中的混淆问题,显著提高 OCR 结果的准确性和可读性。结合高质量的图像输入、适当的预处理和灵活的 PSM 模式选择,开发者可以构建出更加健壮和高效的 OCR 解决方案。在实际应用中,建议对不同 PSM 模式进行测试,以确定最符合特定文档类型和质量的配置。

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

WorkBuddy
WorkBuddy

腾讯云推出的AI原生桌面智能体工作台

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
js正则表达式
js正则表达式

php中文网为大家提供各种js正则表达式语法大全以及各种js正则表达式使用的方法,还有更多js正则表达式的相关文章、相关下载、相关课程,供大家免费下载体验。

530

2023.06.20

js获取当前时间
js获取当前时间

JS全称JavaScript,是一种具有函数优先的轻量级,解释型或即时编译型的编程语言;它是一种属于网络的高级脚本语言,主要用于Web,常用来为网页添加各式各样的动态功能。js怎么获取当前时间呢?php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

576

2023.07.28

js 字符串转数组
js 字符串转数组

js字符串转数组的方法:1、使用“split()”方法;2、使用“Array.from()”方法;3、使用for循环遍历;4、使用“Array.split()”方法。本专题为大家提供js字符串转数组的相关的文章、下载、课程内容,供大家免费下载体验。

760

2023.08.03

js是什么意思
js是什么意思

JS是JavaScript的缩写,它是一种广泛应用于网页开发的脚本语言。JavaScript是一种解释性的、基于对象和事件驱动的编程语言,通常用于为网页增加交互性和动态性。它可以在网页上实现复杂的功能和效果,如表单验证、页面元素操作、动画效果、数据交互等。

6228

2023.08.17

js删除节点的方法
js删除节点的方法

js删除节点的方法有:1、removeChild()方法,用于从父节点中移除指定的子节点,它需要两个参数,第一个参数是要删除的子节点,第二个参数是父节点;2、parentNode.removeChild()方法,可以直接通过父节点调用来删除子节点;3、remove()方法,可以直接删除节点,而无需指定父节点;4、innerHTML属性,用于删除节点的内容。

492

2023.09.01

js截取字符串的方法
js截取字符串的方法

js截取字符串的方法有substring()方法、substr()方法、slice()方法、split()方法和slice()方法。本专题为大家提供字符串相关的文章、下载、课程内容,供大家免费下载体验。

221

2023.09.04

Js中concat和push的区别
Js中concat和push的区别

Js中concat和push的区别:1、concat用于将两个或多个数组合并成一个新数组,并返回这个新数组,而push用于向数组的末尾添加一个或多个元素,并返回修改后的数组的新长度;2、concat不会修改原始数组,是创建新的数组,而push会修改原数组,将新元素添加到原数组的末尾等等。本专题为大家提供concat和push相关的文章、下载、课程内容,供大家免费下载体验。

240

2023.09.14

js截取字符串的方法介绍
js截取字符串的方法介绍

JavaScript字符串截取方法,包括substring、slice、substr、charAt和split方法。这些方法可以根据具体需求,灵活地截取字符串的不同部分。在实际开发中,根据具体情况选择合适的方法进行字符串截取,能够提高代码的效率和可读性 。

303

2023.09.21

C# ASP.NET Core微服务架构与API网关实践
C# ASP.NET Core微服务架构与API网关实践

本专题围绕 C# 在现代后端架构中的微服务实践展开,系统讲解基于 ASP.NET Core 构建可扩展服务体系的核心方法。内容涵盖服务拆分策略、RESTful API 设计、服务间通信、API 网关统一入口管理以及服务治理机制。通过真实项目案例,帮助开发者掌握构建高可用微服务系统的关键技术,提高系统的可扩展性与维护效率。

76

2026.03.11

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
React 教程
React 教程

共58课时 | 6万人学习

国外Web开发全栈课程全集
国外Web开发全栈课程全集

共12课时 | 1万人学习

React核心原理新老生命周期精讲
React核心原理新老生命周期精讲

共12课时 | 1.1万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号