讲师中心 微信公众号
AI工具推荐 视频效率加速

Tesseract.js 多栏文本识别优化指南:活用页面分段模式 (PSM)

雨雪同学_1584

雨雪同学_1584

发布时间:2025-11-21 14:09:06

|

618人浏览过

|

来源于php中文网

原创

Tesseract.js 多栏文本识别优化指南:活用页面分段模式 (PSM)

本文旨在解决 tesseract.js 在处理多栏图像时文本混淆的问题。核心解决方案是调整 tesseract 的页面分段模式 (psm),从默认的 `psm_single_block` 切换到更适合多栏布局的模式,例如 `psm_auto_osd`。文章将详细解释 psm 的作用、常用模式及其在 tesseract.js 中的实现方法,以帮助开发者提高 ocr 准确性。

理解 Tesseract OCR 中的页面分段模式 (PSM)

在使用 Tesseract.js 进行光学字符识别 (OCR) 时,开发者常会遇到一个挑战:当图像包含多栏文本(如报纸、杂志或学术论文)时,识别出的文本可能会出现混淆,不同栏目的内容被错误地合并或交叉。这通常是因为 Tesseract 默认的页面处理方式未能正确识别图像的布局结构。

Tesseract 引擎通过“页面分段模式”(Page Segmentation Mode, 简称 PSM)来确定如何将图像分解成可识别的文本块。不同的 PSM 值指示 Tesseract 采用不同的策略来解析页面布局,这对于提高多栏文本的识别准确性至关关重要。

默认情况下,Tesseract.js 通常使用 PSM_SINGLE_BLOCK(模式 3),这意味着它假设图像中的所有文本都属于一个单一的、统一的文本块。对于单栏或结构简单的图像,这种模式表现良好。然而,对于包含多栏的复杂布局,PSM_SINGLE_BLOCK 无法区分不同的栏目,从而导致文本混合。

优化多栏文本识别:选择合适的 PSM

为了解决多栏文本混淆的问题,我们需要明确告诉 Tesseract 图像具有更复杂的布局。以下是一些推荐的 PSM 模式,特别适用于多栏文本识别:

  • PSM_AUTO_OSD (模式 1): 自动进行页面方向和脚本检测 (OSD),然后进行页面分段。这种模式对于不确定页面方向或布局的图像非常有用,它会尝试自动识别并处理多栏结构。在实践中,它常能有效分离左右两栏的文本。
  • PSM_AUTO_ONLY (模式 2): 仅进行自动页面分段,不进行 OSD。如果已经确定了页面方向,可以使用此模式。
  • PSM_AUTO (模式 3): 默认模式,与 PSM_SINGLE_BLOCK 行为类似,假设一个统一的文本块。
  • PSM_SINGLE_COLUMN (模式 4): 假设图像包含一个单一的文本列。这对于单列文档有效,但对于多列文档则不适用。
  • PSM_SPARSE_TEXT (模式 11): 将图像视为稀疏文本,即不规则分布的文本块。Tesseract 会寻找尽可能多的文本,而不假设任何特定的页面结构。这对于提取少量散布在图像中的文本很有用。
  • PSM_SPARSE_TEXT_OSD (模式 12): 与 PSM_SPARSE_TEXT 类似,但会进行 OSD。

对于多栏图像,最推荐的起点是 PSM_AUTO_OSD。它能够智能地检测页面布局并尝试将其分解为逻辑单元。

JS X Monitor
JS X Monitor

X.com (Twitter) 账号监控自动化:定时抓取指定账号最新推文,发现新内容即时推送通知。

下载

在 Tesseract.js 中实现 PSM 配置

在 Tesseract.js 中,可以通过 worker.setParameters() 方法或直接在 Tesseract.recognize() 函数的第三个参数(一个选项对象)中设置 PSM。PSM 值通过 tessedit_pageseg_mode 参数来指定。

以下是如何修改您提供的 React 组件代码,以引入 PSM 配置:

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

// ... (languageOptions 和其他组件代码保持不变) ...

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([]);
  // 新增状态用于存储选定的PSM模式
  const [selectedPsm, setSelectedPsm] = useState({ value: 1, label: "PSM_AUTO_OSD" }); // 默认选择 PSM_AUTO_OSD

  // PSM 选项
  const psmOptions = [
    { value: 0, label: "OSD_ONLY" },
    { value: 1, label: "PSM_AUTO_OSD" },
    { value: 2, label: "PSM_AUTO_ONLY" },
    { value: 3, label: "PSM_AUTO (Default)" },
    { value: 4, label: "PSM_SINGLE_COLUMN" },
    { value: 5, label: "PSM_SINGLE_BLOCK_VERT_TEXT" },
    { value: 6, label: "PSM_SINGLE_BLOCK" },
    { value: 7, label: "PSM_SINGLE_LINE" },
    { value: 8, label: "PSM_SINGLE_WORD" },
    { value: 9, label: "PSM_CIRCLE_WORD" },
    { value: 10, label: "PSM_SINGLE_CHAR" },
    { value: 11, label: "PSM_SPARSE_TEXT" },
    { value: 12, label: "PSM_SPARSE_TEXT_OSD" },
    { value: 13, label: "PSM_RAW_LINE" },
  ];

  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("");
    setSelectedPsm({ value: 1, label: "PSM_AUTO_OSD" }); // 重置PSM
    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 {
          const result = await Tesseract.recognize(
            image,
            selectedLanguages.map((lang) => lang.value).join("+"),
            {
              // 在这里设置页面分段模式 (PSM)
              tessedit_pageseg_mode: selectedPsm.value,
            }
          );
          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>
          )}

          {/* 新增 PSM 选择器 */}
          <Select
            options={psmOptions}
            value={selectedPsm}
            onChange={setSelectedPsm}
            placeholder="Select Page Segmentation Mode..."
            className="mt-3"
          />

          <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. 新增 psmOptions 数组和 selectedPsm 状态: 用于管理和显示可用的 PSM 模式及其当前选择。
  2. 在 Tesseract.recognize() 中添加 tessedit_pageseg_mode 参数:
    const result = await Tesseract.recognize(
      image,
      selectedLanguages.map((lang) => lang.value).join("+"),
      {
        tessedit_pageseg_mode: selectedPsm.value, // 关键:设置 PSM
      }
    );

    通过将 tessedit_pageseg_mode 设置为 selectedPsm.value,我们允许用户动态选择最适合其图像的 PSM。

  3. 在 UI 中添加 PSM 选择器: 在语言选择器下方添加一个 Select 组件,让用户可以选择不同的 PSM 模式。

注意事项与最佳实践

  • 实验是关键: 不同的多栏图像(如文本密度、字体、栏间距等)可能对不同的 PSM 模式有不同的响应。始终建议对您的特定数据集进行实验,以找到最佳的 PSM。
  • 图像预处理: 在进行 OCR 之前,对图像进行预处理可以显著提高识别准确性。这包括:
    • 二值化: 将彩色或灰度图像转换为纯黑白图像。
    • 去噪: 移除图像中的杂点。
    • 倾斜校正 (Deskewing): 纠正图像的轻微倾斜。
    • 裁剪: 移除图像边缘的无关区域。
  • 语言包: 确保您加载了正确的语言包。对于混合语言的文档,可以同时加载多个语言包,例如 eng+chi_sim。
  • Tesseract 版本: 随着 Tesseract 引擎的不断发展,新版本通常会带来更好的识别能力和更智能的页面分析。确保使用最新稳定版本的 Tesseract.js。
  • 性能考量: 某些 PSM 模式(如 PSM_AUTO_OSD)可能需要更多的计算资源和时间来分析页面布局,尤其是在处理高分辨率图像时。在对性能有严格要求的场景中,需要进行权衡。

总结

Tesseract.js 在处理多栏图像时,通过合理配置页面分段模式 (PSM) 可以有效解决文本混淆的问题。将默认的 PSM_SINGLE_BLOCK 切换到更智能的模式,如 PSM_AUTO_OSD,能够显著提高 OCR 的准确性和文本结构的还原度。开发者应结合图像特点,积极尝试不同的 PSM 模式,并辅以必要的图像预处理,以达到最佳的识别效果。

热门AI工具

更多
UP简历
UP简历 Hot

一款AI办公效率工具,主要用于基于AI技术的免费在线简历制作工具,适合需要提升相关任务效率的用户。

创客贴AI

创客贴AI是一款AI图像与设计工具,AI辅助的智能在线设计工具。

AionClaw
AionClaw Hot

AionClaw是一款面向办公、创作和编程任务的AI桌面智能体。

SkildArt
SkildArt Hot

SkildArt是一款AI文本写作工具,一站式 AI 视觉创作平台。

WorkBuddy

一款AI办公效率工具,主要用于腾讯云推出的AI原生桌面智能体工作台,适合需要提升相关任务效率的用户。

豆包大模型

豆包大模型是一款由字节跳动推出的企业级大语言模型服务平台。

讯飞绘文

讯飞绘文是一款由科大讯飞推出的一站式 AIGC 内容运营平台。

DeepSeek

DeepSeek是一款面向对话、写作、编程和推理场景的AI大模型工具。

立刻MV
立刻MV Hot

立刻MV是一款AI文本写作工具,AI 音乐视频(MV)创作工具。

相关专题

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

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

4516

2023.06.20

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

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

1375

2023.07.28

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

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

1798

2023.08.03

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

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

10063

2023.08.17

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

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

980

2023.09.01

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

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

2804

2023.09.04

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

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

1589

2023.09.14

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

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

4829

2023.09.21

Gradle插件开发入门教程
Gradle插件开发入门教程

本专题介绍Gradle插件开发的基本流程,涵盖插件类编写、自定义任务注册、扩展参数设计和插件应用方法,帮助你理解插件如何组织构建逻辑,减少多个项目之间的重复配置。

60

2026.10.22

热门下载

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

精品课程

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

共58课时 | 12.3万人学习

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

共12课时 | 1.4万人学习

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

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn