/** * 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();