152 lines
5.5 KiB
JavaScript
152 lines
5.5 KiB
JavaScript
/**
|
||
* test_real_images.js — 使用真实试纸图片验证 PaperRecognizer
|
||
*
|
||
* 用法: node test_real_images.js [image_path]
|
||
* 不带参数则处理 /home/ubuntu/nanocode/web-opencv/cpp/images/ 下所有图片
|
||
* 带参数则只处理指定图片
|
||
*
|
||
* 依赖: npm install canvas
|
||
*/
|
||
|
||
const { createCanvas, loadImage } = require('canvas');
|
||
const fs = require('fs');
|
||
const path = require('path');
|
||
const { PaperRecognizer, PST_NAMES } = require('./paper-recognizer.js');
|
||
|
||
const IMAGE_DIR = '/home/ubuntu/nanocode/web-opencv/cpp/images/';
|
||
const OUTPUT_DIR = '/home/ubuntu/nanocode/web-opencv/miniappB/samples/';
|
||
|
||
// Ensure output dir
|
||
if (!fs.existsSync(OUTPUT_DIR)) fs.mkdirSync(OUTPUT_DIR, { recursive: true });
|
||
|
||
const LOCATOR_COLORS = ['Unknown', 'Red', 'Green', 'Blue'];
|
||
const BLUR_NAMES = ['None', 'Slight', 'Severe'];
|
||
|
||
async function processImage(imagePath) {
|
||
const fname = path.basename(imagePath);
|
||
console.log(`\n▶ ${fname}`);
|
||
|
||
const t0 = performance.now();
|
||
|
||
// Load image
|
||
const img = await loadImage(imagePath);
|
||
console.log(` 尺寸: ${img.width}×${img.height}`);
|
||
|
||
// Draw to canvas to get RGBA pixels
|
||
const canvas = createCanvas(img.width, img.height);
|
||
const ctx = canvas.getContext('2d');
|
||
ctx.drawImage(img, 0, 0);
|
||
const imageData = ctx.getImageData(0, 0, img.width, img.height);
|
||
const pixels = new Uint8Array(imageData.data.buffer);
|
||
|
||
// Recognize
|
||
const recognizer = new PaperRecognizer();
|
||
const result = recognizer.recognize(pixels, img.width, img.height);
|
||
const elapsed = (performance.now() - t0).toFixed(0);
|
||
|
||
const paperType = PST_NAMES[result.paperType] || 'UNKNOWN';
|
||
const color = LOCATOR_COLORS[result.locatorColor] || '?';
|
||
const blur = BLUR_NAMES[result.blurLevel] || '?';
|
||
|
||
if (result.success) {
|
||
console.log(` ✅ 成功 | 类型: ${paperType} | 色块: ${result.itemCount} | 定位块颜色: ${color} | 小在左: ${result.smallLocatorOnLeft} | 模糊: ${blur}(${(result.blurScore||0).toFixed(0)}) | 耗时: ${elapsed}ms`);
|
||
|
||
// Save annotated image
|
||
if (recognizer.annotatedPixels) {
|
||
const outCanvas = createCanvas(img.width, img.height);
|
||
const outCtx = outCanvas.getContext('2d');
|
||
const outData = outCtx.createImageData(img.width, img.height);
|
||
// annotatedPixels is BGRA, need to convert to RGBA for canvas
|
||
const ap = recognizer.annotatedPixels;
|
||
for (let i = 0; i < ap.length; i += 4) {
|
||
outData.data[i] = ap[i + 2]; // R
|
||
outData.data[i + 1] = ap[i + 1]; // G
|
||
outData.data[i + 2] = ap[i]; // B
|
||
outData.data[i + 3] = ap[i + 3]; // A
|
||
}
|
||
outCtx.putImageData(outData, 0, 0);
|
||
const outName = fname.replace(/\.(jpg|jpeg|png|JPG|JPEG|PNG)$/, '_annotated.png');
|
||
const outPath = path.join(OUTPUT_DIR, outName);
|
||
const buf = outCanvas.toBuffer('image/png');
|
||
fs.writeFileSync(outPath, buf);
|
||
console.log(` 标注图: ${outPath}`);
|
||
}
|
||
|
||
// Print first 3 colors
|
||
if (result.colors && result.colors.length > 0) {
|
||
const preview = result.colors.slice(0, 3).map(c =>
|
||
`RGB(${c.r.toFixed(0)},${c.g.toFixed(0)},${c.b_val.toFixed(0)})`
|
||
).join(', ');
|
||
console.log(` 前3色块: ${preview}${result.colors.length > 3 ? ' ...' : ''}`);
|
||
}
|
||
} else {
|
||
const errCode = result.errorCode;
|
||
const errMsg = result.errorMessage || '';
|
||
console.log(` ❌ 失败 | 错误码: ${errCode} | ${errMsg} | 耗时: ${elapsed}ms`);
|
||
}
|
||
|
||
recognizer.free();
|
||
return { fname, result, elapsed };
|
||
}
|
||
|
||
async function main() {
|
||
const args = process.argv.slice(2);
|
||
let files;
|
||
|
||
if (args.length > 0) {
|
||
files = args.filter(f => {
|
||
const ext = path.extname(f).toLowerCase();
|
||
return ['.jpg', '.jpeg', '.png'].includes(ext);
|
||
});
|
||
} else {
|
||
files = fs.readdirSync(IMAGE_DIR)
|
||
.filter(f => /\.(jpg|jpeg|png|JPG)$/i.test(f))
|
||
.map(f => path.join(IMAGE_DIR, f));
|
||
}
|
||
|
||
if (files.length === 0) {
|
||
console.log('未找到图片文件');
|
||
process.exit(1);
|
||
}
|
||
|
||
console.log(`═══════════════════════════════════════════`);
|
||
console.log(`PaperRecognizer 真实图片测试`);
|
||
console.log(`共 ${files.length} 张图片`);
|
||
console.log(`═══════════════════════════════════════════`);
|
||
|
||
const results = [];
|
||
for (const f of files) {
|
||
try {
|
||
const r = await processImage(f);
|
||
results.push(r);
|
||
} catch (e) {
|
||
console.log(`\n▶ ${path.basename(f)}`);
|
||
console.log(` 💥 异常: ${e.message}`);
|
||
results.push({ fname: path.basename(f), result: null, error: e.message });
|
||
}
|
||
}
|
||
|
||
// Summary
|
||
const success = results.filter(r => r.result && r.result.success).length;
|
||
const failed = results.filter(r => r.result && !r.result.success).length;
|
||
const errors = results.filter(r => !r.result).length;
|
||
const total = results.length;
|
||
|
||
console.log(`\n═══════════════════════════════════════════`);
|
||
console.log(`汇总: ${total} 张 | ✅ ${success} | ❌ ${failed} | 💥 ${errors}`);
|
||
console.log(`成功率: ${(success / total * 100).toFixed(1)}%`);
|
||
|
||
// Print failures detail
|
||
const failures = results.filter(r => r.result && !r.result.success);
|
||
if (failures.length > 0) {
|
||
console.log(`\n失败详情:`);
|
||
for (const f of failures) {
|
||
console.log(` ${f.fname}: [${f.result.errorCode}] ${f.result.errorMessage}`);
|
||
}
|
||
}
|
||
|
||
process.exit(0);
|
||
}
|
||
|
||
main();
|