0

0

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

霞舞

霞舞

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

|

538人浏览过

|

来源于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。它能够智能地检测页面布局并尝试将其分解为逻辑单元。

在 Tesseract.js 中实现 PSM 配置

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

PaperFake
PaperFake

AI写论文

下载

以下是如何修改您提供的 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工具

更多
DeepSeek
DeepSeek

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

豆包大模型
豆包大模型

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

WorkBuddy
WorkBuddy

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

腾讯元宝
腾讯元宝

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

文心一言
文心一言

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

讯飞写作
讯飞写作

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

即梦AI
即梦AI

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

ChatGPT
ChatGPT

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

相关专题

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

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

531

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字符串转数组的相关的文章、下载、课程内容,供大家免费下载体验。

761

2023.08.03

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

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

6258

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

TypeScript类型系统进阶与大型前端项目实践
TypeScript类型系统进阶与大型前端项目实践

本专题围绕 TypeScript 在大型前端项目中的应用展开,深入讲解类型系统设计与工程化开发方法。内容包括泛型与高级类型、类型推断机制、声明文件编写、模块化结构设计以及代码规范管理。通过真实项目案例分析,帮助开发者构建类型安全、结构清晰、易维护的前端工程体系,提高团队协作效率与代码质量。

26

2026.03.13

热门下载

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

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
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号