PRA-only-demo/src/main/capabilities/ocr-caps.ts

72 lines
3.0 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import Tesseract from "tesseract.js";
import fs from "node:fs";
import { register } from "../engine/registry";
import { RuntimeError } from "../engine/types";
interface OcrWord { text: string; x: number; y: number; width: number; height: number; confidence: number }
function requireImage(args: Record<string, unknown>, nodeId: string): string {
for (const key of ["image_path", "imagePath", "file_path", "filePath", "path", "image", "pictureSource"]) {
const v = String(args[key] ?? "").trim();
if (v) return v;
}
throw new RuntimeError("validation_error", "图片路径为空", { nodeId, componentId: nodeId, recommendation: "填写本地图片路径(png/jpg/bmp)" });
}
async function recognizeWithLayout(imagePath: string, nodeId: string): Promise<{ text: string; words: OcrWord[]; textLines: string[] }> {
if (!fs.existsSync(imagePath)) {
throw new RuntimeError("file_not_found", "图片不存在:" + imagePath, { nodeId, componentId: nodeId });
}
const worker = await Tesseract.createWorker("chi_sim+eng");
try {
const result = await worker.recognize(imagePath, {}, { text: true, blocks: true });
const data = result.data as {
text?: string;
blocks?: Array<{
paragraphs?: Array<{
lines?: Array<{
words?: Array<{ text: string; confidence: number; bbox: { x0: number; y0: number; x1: number; y1: number } }>;
}>;
}>;
}>;
};
const words: OcrWord[] = [];
const textLines: string[] = [];
for (const block of data.blocks ?? []) {
for (const para of block.paragraphs ?? []) {
for (const line of para.lines ?? []) {
const lineWords = line.words ?? [];
textLines.push(lineWords.map(w => w.text).join(" "));
for (const w of lineWords) {
words.push({ text: w.text, x: w.bbox.x0, y: w.bbox.y0, width: w.bbox.x1 - w.bbox.x0, height: w.bbox.y1 - w.bbox.y0, confidence: w.confidence });
}
}
}
}
return { text: (data.text ?? "").trim(), words, textLines };
} finally {
await worker.terminate();
}
}
register("OcrOffline_GetOcrTextNew", async (ctx, node, args) => {
const imagePath = requireImage(args, node.id);
const result = await recognizeWithLayout(imagePath, node.id);
ctx.log("OCR 识别完成:" + result.text.length + " 字符 / " + result.words.length + " 词", "info", node.id);
return result.text;
});
register("OcrOffline_GetOcrTextPosition", async (ctx, node, args) => {
const imagePath = requireImage(args, node.id);
const result = await recognizeWithLayout(imagePath, node.id);
ctx.log("OCR 定位完成:" + result.words.length + " 个词", "info", node.id);
return result.words;
});
register("OcrOffline_GetOcrTable", async (ctx, node, args) => {
const imagePath = requireImage(args, node.id);
const result = await recognizeWithLayout(imagePath, node.id);
const rows = result.textLines.map(line => line.split(/ {2,}/).filter(Boolean).join("\t"));
ctx.log("OCR 表格识别完成:" + rows.length + " 行", "info", node.id);
return rows;
});