picturereader 1.0.1 → 1.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -24
- package/package.json +2 -1
- package/scripts/preview.mjs +40 -0
- package/scripts/setup-ocr.mjs +96 -0
- package/src/core.js +1434 -1427
package/README.md
CHANGED
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
| 版本 | 平台 | 形态 | 源码 | 安装 |
|
|
16
16
|
|---|---|---|---|---|
|
|
17
17
|
| **DSH 版** | DeepSeek Harness(含 EAC 桌面端) | npm 插件(`dsh.bundle`) | 仓库根目录 | `dsh plugin --profile web add picturereader` |
|
|
18
|
-
| **ZCode 版** | ZCode 桌面端 | 本地 marketplace 插件(MCP server + skill) |
|
|
18
|
+
| **ZCode 版** | ZCode 桌面端 | 本地 marketplace 插件(MCP server + skill) | [jing-hy/picturereader-zcode](https://github.com/jing-hy/picturereader-zcode)(独立仓库) | 见其 README |
|
|
19
19
|
|
|
20
20
|
两个版本共用同一套业务核心(`src/core.js`)与读图方法论 skill(`image-reading`),
|
|
21
21
|
三个工具行为完全一致:`image_scan` / `image_ocr` / `image_sample`。
|
|
@@ -72,26 +72,11 @@ copy skills\image-reading.md %USERPROFILE%\.dsh\skills\ # Windows
|
|
|
72
72
|
|
|
73
73
|
### ZCode 版
|
|
74
74
|
|
|
75
|
-
ZCode
|
|
75
|
+
ZCode 版是**独立仓库**:[jing-hy/picturereader-zcode](https://github.com/jing-hy/picturereader-zcode)。
|
|
76
76
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
→ 市场列表出现 picturereader → 安装
|
|
81
|
-
```
|
|
82
|
-
|
|
83
|
-
安装并启用后(重启 ZCode 或按提示重连):
|
|
84
|
-
|
|
85
|
-
1. **MCP server `picturereader` 自动连接**(stdio 子进程 `node mcp/server.js`),模型工具列表出现 `image_scan` / `image_ocr` / `image_sample`;
|
|
86
|
-
2. **skill `image-reading` 自动出现**在技能列表(图片任务自动触发);
|
|
87
|
-
3. **(可选)PaddleOCR 增强引擎**:`node scripts/setup-ocr.mjs`。
|
|
88
|
-
|
|
89
|
-
> **关于安装后的运行位置**:市场安装会把插件目录拷贝到 ZCode 插件缓存
|
|
90
|
-
> (`~/.zcode/cli/plugins/cache/...`),但 `.zcode-plugin/plugin.json` 里 MCP server 的
|
|
91
|
-
> `args` 是**具体绝对路径**,指向所选目录的 `mcp/server.js`——因此 MCP server 始终从
|
|
92
|
-
> **所选目录**运行:改 `src/core.js` 下次调用即生效(热加载)。skill / 工具定义变更
|
|
93
|
-
> 需在 Discover 里卸载后重装生效(或手动同步缓存副本)。若插件目录移动,请同步更新
|
|
94
|
-
> `plugin.json` 里的 `args` 路径。
|
|
77
|
+
ZCode 版通过 **MCP server**(`mcp/server.js`,stdio)把三个工具暴露给 ZCode,
|
|
78
|
+
读图方法论作为 **skill**(`skills/image-reading/`)随插件分发,业务逻辑 `src/core.js`
|
|
79
|
+
与本仓库完全一致。安装与使用请见其 README。
|
|
95
80
|
|
|
96
81
|
## 使用
|
|
97
82
|
|
|
@@ -123,14 +108,15 @@ luminance grid / color grid
|
|
|
123
108
|
## 开发
|
|
124
109
|
|
|
125
110
|
```sh
|
|
126
|
-
# DSH
|
|
111
|
+
# DSH 版(本仓库)
|
|
127
112
|
npm install
|
|
128
113
|
npm test # node:test,76 个测试全绿
|
|
129
114
|
node scripts/setup-ocr.mjs # 可选:装 PaddleOCR
|
|
130
115
|
node scripts/preview.mjs # 生成 fixtures 并预览渲染
|
|
131
116
|
|
|
132
|
-
# ZCode
|
|
133
|
-
|
|
117
|
+
# ZCode 版(独立仓库 jing-hy/picturereader-zcode)
|
|
118
|
+
git clone https://github.com/jing-hy/picturereader-zcode.git
|
|
119
|
+
cd picturereader-zcode
|
|
134
120
|
npm install
|
|
135
121
|
npm test # node:test
|
|
136
122
|
node scripts/setup-ocr.mjs # 可选
|
|
@@ -141,7 +127,7 @@ node scripts/setup-ocr.mjs # 可选
|
|
|
141
127
|
下次调用即生效**;工具定义(schema/描述)改动需重启桌面端。
|
|
142
128
|
|
|
143
129
|
**热插拔(ZCode 版)**:MCP server 从所选目录运行,改 `src/core.js` 下次调用即生效
|
|
144
|
-
|
|
130
|
+
(详见 picturereader-zcode 仓库 README)。
|
|
145
131
|
|
|
146
132
|
## 优势
|
|
147
133
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "picturereader",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.3",
|
|
4
4
|
"description": "DSH plugin: pixel-to-text image reading for text-only models. Downscales and color-quantizes PNG/JPEG/GIF/BMP and feeds the coarse pixel grid to the model so DeepSeek can \u0027see\u0027 layout, colors and rough shapes without a vision model.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.js",
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
},
|
|
12
12
|
"files": [
|
|
13
13
|
"src",
|
|
14
|
+
"scripts",
|
|
14
15
|
"cordis.patch.yml",
|
|
15
16
|
"skills",
|
|
16
17
|
"README.md",
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generate fixture images on disk and print the image_scan render for a
|
|
3
|
+
* quick visual sanity check without booting a harness.
|
|
4
|
+
* Usage: node scripts/preview.mjs
|
|
5
|
+
* @module picturereader/scripts/preview
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { mkdirSync, writeFileSync } from 'node:fs';
|
|
9
|
+
import { join, dirname } from 'node:path';
|
|
10
|
+
import { fileURLToPath } from 'node:url';
|
|
11
|
+
import { analyzeImage, renderImageScan } from '../src/core.js';
|
|
12
|
+
import { makeChartRgba, makeQuadrantRgba, pngFromRgba, jpegFromRgba, gifFromRgba, bmpFromRgba } from '../tests/fixtures.mjs';
|
|
13
|
+
|
|
14
|
+
const outDir = join(dirname(fileURLToPath(import.meta.url)), '..', 'tests', 'fixtures-out');
|
|
15
|
+
mkdirSync(outDir, { recursive: true });
|
|
16
|
+
|
|
17
|
+
const images = [
|
|
18
|
+
{ name: 'chart', width: 600, height: 400, rgba: makeChartRgba(), formats: ['png', 'jpeg', 'gif', 'bmp'] },
|
|
19
|
+
{ name: 'quadrant', width: 100, height: 100, rgba: makeQuadrantRgba(), formats: ['png'] }
|
|
20
|
+
];
|
|
21
|
+
|
|
22
|
+
for (const image of images) {
|
|
23
|
+
for (const format of image.formats) {
|
|
24
|
+
const buffer =
|
|
25
|
+
format === 'png' ? pngFromRgba(image.width, image.height, image.rgba)
|
|
26
|
+
: format === 'jpeg' ? jpegFromRgba(image.width, image.height, image.rgba)
|
|
27
|
+
: format === 'gif' ? gifFromRgba(image.width, image.height, image.rgba)
|
|
28
|
+
: bmpFromRgba(image.width, image.height, image.rgba, 24);
|
|
29
|
+
writeFileSync(join(outDir, `${image.name}.${format}`), buffer);
|
|
30
|
+
}
|
|
31
|
+
const analysis = analyzeImage(image.rgba, image.width, image.height, { size: 32, mode: 'auto', region: undefined });
|
|
32
|
+
console.log(renderImageScan({
|
|
33
|
+
path: join(outDir, `${image.name}.png`),
|
|
34
|
+
width: image.width,
|
|
35
|
+
height: image.height,
|
|
36
|
+
region: 'full',
|
|
37
|
+
...analysis
|
|
38
|
+
}));
|
|
39
|
+
console.log();
|
|
40
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Optional install helper for the PaddleOCR engine (image_ocr engine="paddle").
|
|
3
|
+
* PaddleOCR is RECOMMENDED (far better at glowing/curved/game text) but
|
|
4
|
+
* OPTIONAL — image_ocr degrades to the Windows engine when it is missing.
|
|
5
|
+
*
|
|
6
|
+
* What this does:
|
|
7
|
+
* 1. Ensures a Python 3.12+ interpreter exists (downloads the official
|
|
8
|
+
* 3.12.10 installer from the npmmirror mirror if missing).
|
|
9
|
+
* 2. Creates/repairs the paddle_venv.
|
|
10
|
+
* 3. Installs paddlepaddle + paddleocr from the Tsinghua PyPI mirror.
|
|
11
|
+
* 4. Warms the model cache by running one recognition on a test image.
|
|
12
|
+
*
|
|
13
|
+
* Usage: node scripts/setup-ocr.mjs
|
|
14
|
+
*/
|
|
15
|
+
import { existsSync, mkdirSync } from 'node:fs';
|
|
16
|
+
import { spawnSync } from 'node:child_process';
|
|
17
|
+
import { join, dirname } from 'node:path';
|
|
18
|
+
import { fileURLToPath } from 'node:url';
|
|
19
|
+
|
|
20
|
+
const PY312 = 'C:\\Users\\Administrator\\Python312\\python.exe';
|
|
21
|
+
const VENV = 'C:\\Users\\Administrator\\paddle_venv\\Scripts\\python.exe';
|
|
22
|
+
const INSTALLER = 'C:\\Users\\Administrator\\Downloads\\python-3.12.10-amd64.exe';
|
|
23
|
+
const INSTALLER_URL = 'https://registry.npmmirror.com/-/binary/python/3.12.10/python-3.12.10-amd64.exe';
|
|
24
|
+
const PYPI = 'https://pypi.tuna.tsinghua.edu.cn/simple';
|
|
25
|
+
const CACHE = join(dirname(fileURLToPath(import.meta.url)), '..', '.paddlex-cache');
|
|
26
|
+
|
|
27
|
+
function run(cmd, args, opts = {}) {
|
|
28
|
+
console.log(`> ${cmd} ${args.join(' ')}`);
|
|
29
|
+
const result = spawnSync(cmd, args, { stdio: 'inherit', ...opts });
|
|
30
|
+
if (result.status !== 0) {
|
|
31
|
+
console.error(`!! command failed (exit ${result.status})`);
|
|
32
|
+
process.exit(1);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// 1. base Python 3.12
|
|
37
|
+
if (!existsSync(PY312)) {
|
|
38
|
+
console.log('[1/4] Python 3.12 missing — downloading installer (npmmirror mirror)...');
|
|
39
|
+
run('curl.exe', ['-L', '-o', INSTALLER, INSTALLER_URL]);
|
|
40
|
+
console.log('[1/4] Installing Python 3.12 to C:\\Users\\Administrator\\Python312 (user-level, silent)...');
|
|
41
|
+
spawnSync(INSTALLER, [
|
|
42
|
+
'/quiet', 'InstallAllUsers=0', 'TargetDir=C:\\Users\\Administrator\\Python312',
|
|
43
|
+
'Include_pip=1', 'PrependPath=0', 'Include_test=0', 'Include_launcher=0'
|
|
44
|
+
], { stdio: 'inherit' });
|
|
45
|
+
if (!existsSync(PY312)) {
|
|
46
|
+
console.error('!! Python install did not produce ' + PY312);
|
|
47
|
+
process.exit(1);
|
|
48
|
+
}
|
|
49
|
+
} else {
|
|
50
|
+
console.log('[1/4] Python 3.12 found at ' + PY312);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// 2. venv
|
|
54
|
+
if (!existsSync(VENV)) {
|
|
55
|
+
console.log('[2/4] Creating paddle_venv...');
|
|
56
|
+
mkdirSync(dirname(VENV), { recursive: true });
|
|
57
|
+
run(PY312, ['-m', 'venv', 'C:\\Users\\Administrator\\paddle_venv']);
|
|
58
|
+
} else {
|
|
59
|
+
console.log('[2/4] paddle_venv found');
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// 3. paddlepaddle + paddleocr
|
|
63
|
+
const probe = spawnSync(VENV, ['-c', 'import paddleocr; print(paddleocr.__version__)'], { encoding: 'utf8' });
|
|
64
|
+
if (probe.status !== 0) {
|
|
65
|
+
console.log('[3/4] Installing paddlepaddle + paddleocr (Tsinghua mirror, ~1-3 min)...');
|
|
66
|
+
run(VENV, ['-m', 'pip', 'install', '-i', PYPI, '--upgrade', 'pip']);
|
|
67
|
+
run(VENV, ['-m', 'pip', 'install', '-i', PYPI, 'paddlepaddle==3.3.1', 'paddleocr']);
|
|
68
|
+
} else {
|
|
69
|
+
console.log(`[3/4] paddleocr already installed (${probe.stdout.trim()})`);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// 4. warm the model cache with one recognition
|
|
73
|
+
console.log('[4/4] Warming the model cache (first run downloads detection/recognition models)...');
|
|
74
|
+
const testImage = join(dirname(fileURLToPath(import.meta.url)), '..', 'tests', 'fixtures-out', 'ocr-test.png');
|
|
75
|
+
mkdirSync(join(CACHE), { recursive: true });
|
|
76
|
+
if (existsSync(testImage)) {
|
|
77
|
+
const warm = spawnSync(VENV, ['-c', [
|
|
78
|
+
'from paddleocr import PaddleOCR',
|
|
79
|
+
"ocr = PaddleOCR(lang='ch', use_doc_orientation_classify=False, use_doc_unwarping=False, use_textline_orientation=False, enable_mkldnn=False)",
|
|
80
|
+
`result = ocr.predict(r'${testImage.replaceAll("'", "''")}')`,
|
|
81
|
+
'print("warm-up OCR ok, lines:", sum(len(r.get("rec_texts") or []) for r in result))'
|
|
82
|
+
].join('; ')], {
|
|
83
|
+
env: { ...process.env, PADDLE_PDX_CACHE_HOME: CACHE, PYTHONIOENCODING: 'utf-8' },
|
|
84
|
+
encoding: 'utf8'
|
|
85
|
+
});
|
|
86
|
+
if (warm.status !== 0) {
|
|
87
|
+
console.error('!! warm-up failed — see output above; the engine may still work once models download');
|
|
88
|
+
process.exit(1);
|
|
89
|
+
}
|
|
90
|
+
console.log(warm.stdout.trim());
|
|
91
|
+
} else {
|
|
92
|
+
console.log('[4/4] test image missing — skip warm-up (first image_ocr paddle call will download models)');
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
console.log('\nDone. image_ocr engine="paddle" is now available.');
|
|
96
|
+
console.log('Verify: ask the model to read an image with image_ocr(engine="paddle").');
|