/** * diagnose2.js — 使用 PaperRecognizer 内部函数诊断 */ const { createCanvas, loadImage } = require('canvas'); const fs = require('fs'); const path = require('path'); const { Img, PaperRecognizer } = require('./paper-recognizer.js'); const OUTPUT_DIR = '/home/ubuntu/nanocode/web-opencv/miniappB/samples/'; function saveDebugImage(label, data, w, h, isGray) { const canvas = createCanvas(w, h); const ctx = canvas.getContext('2d'); const imgData = ctx.createImageData(w, h); for (let i = 0; i < w * h; i++) { const val = data[i]; imgData.data[i * 4] = isGray ? val : val; imgData.data[i * 4 + 1] = isGray ? val : val; imgData.data[i * 4 + 2] = isGray ? val : val; imgData.data[i * 4 + 3] = 255; } ctx.putImageData(imgData, 0, 0); const outPath = path.join(OUTPUT_DIR, label + '.png'); fs.writeFileSync(outPath, canvas.toBuffer('image/png')); } // Patch bgrImageToHsv for diagnose Img.bgrImageToHsv_ = Img.bgrImageToHsv || function(bgr, w, h) { const out = new Float64Array(w * h * 3); for (let i = 0; i < w * h; i++) { const j = i * 3; const [hh, ss, vv] = Img.bgrToHsv(bgr[j], bgr[j+1], bgr[j+2]); out[j] = hh; out[j+1] = ss; out[j+2] = vv; } return out; }; async function diagnose(imagePath) { const fname = path.basename(imagePath); console.log(`诊断: ${fname}`); const img = await loadImage(imagePath); const w = img.width, h = img.height; console.log(`尺寸: ${w}×${h}`); const canvas = createCanvas(w, h); const ctx = canvas.getContext('2d'); ctx.drawImage(img, 0, 0); const imgData = ctx.getImageData(0, 0, w, h); const rgba = new Uint8Array(imgData.data.buffer); // Convert RGBA → BGR (matching paper-recognizer preprocess) const bgr = new Float64Array(w * h * 3); const gray = new Float64Array(w * h); for (let i = 0; i < w * h; i++) { const i4 = i * 4, i3 = i * 3; const r = rgba[i4], g = rgba[i4+1], b = rgba[i4+2]; bgr[i3] = b; bgr[i3+1] = g; bgr[i3+2] = r; gray[i] = 0.299 * r + 0.587 * g + 0.114 * b; } // HSV via paper-recognizer's bgrImageToHsv const hsv = Img.bgrImageToHsv(bgr, w, h); // ---- HSV sampling from locator-like regions ---- // Find bright-ish, saturated regions near the center strip console.log(`\n--- HSV 全图统计 ---`); const stats = { h: [], s: [], v: [] }; for (let i = 0; i < w * h; i++) { const j = i * 3; stats.h.push(hsv[j]); stats.s.push(hsv[j+1]); stats.v.push(hsv[j+2]); } stats.h.sort((a,b)=>a-b); stats.s.sort((a,b)=>a-b); stats.v.sort((a,b)=>a-b); const m = stats.h.length; const p5 = Math.floor(m*0.05), p25 = Math.floor(m*0.25), p50 = Math.floor(m*0.5), p75 = Math.floor(m*0.75), p95 = Math.floor(m*0.95); console.log(` H: p5=${stats.h[p5].toFixed(0)} p50=${stats.h[p50].toFixed(0)} p95=${stats.h[p95].toFixed(0)}`); console.log(` S: p5=${stats.s[p5].toFixed(0)} p50=${stats.s[p50].toFixed(0)} p95=${stats.s[p95].toFixed(0)}`); console.log(` V: p5=${stats.v[p5].toFixed(0)} p50=${stats.v[p50].toFixed(0)} p95=${stats.v[p95].toFixed(0)}`); // ---- Color segmentation (matching actual code) ---- console.log(`\n--- 颜色分割 ---`); // Blue mask const blueMask = Img.hsvInRange(hsv, w, h, 180, 260, 30, 255, 30, 220); // Green mask const greenMask = Img.hsvInRange(hsv, w, h, 80, 170, 30, 255, 30, 220); // Red mask (added - paper-recognizer missing this!) const redMask1 = Img.hsvInRange(hsv, w, h, 0, 15, 30, 255, 30, 220); const redMask2 = Img.hsvInRange(hsv, w, h, 340, 360, 30, 255, 30, 220); const redMask = new Uint8Array(w * h); for (let i = 0; i < w * h; i++) redMask[i] = Math.max(redMask1[i], redMask2[i]); const colorMask = new Uint8Array(w * h); for (let i = 0; i < w * h; i++) { colorMask[i] = Math.max(blueMask[i], greenMask[i], redMask[i]); } let bluePx = 0, greenPx = 0, redPx = 0; for (let i = 0; i < w * h; i++) { if (blueMask[i] > 0) bluePx++; if (greenMask[i] > 0) greenPx++; if (redMask[i] > 0) redPx++; } const total = w * h; console.log(` 蓝: ${bluePx} (${(bluePx/total*100).toFixed(2)}%)`); console.log(` 绿: ${greenPx} (${(greenPx/total*100).toFixed(2)}%)`); console.log(` 红: ${redPx} (${(redPx/total*100).toFixed(2)}%)`); console.log(` 总: ${Math.max(bluePx, greenPx, redPx)} (${(Math.max(bluePx, greenPx, redPx)/total*100).toFixed(2)}%)`); saveDebugImage(`diag_${fname.replace(/\.\w+$/,'')}_01_color`, colorMask, w, h, true); // Morphology close const closed = Img.morphClose(colorMask, w, h, 5); const closed2 = Img.morphClose(closed, w, h, 5); saveDebugImage(`diag_${fname.replace(/\.\w+$/,'')}_02_closed`, closed2, w, h, true); // Find contours const contours = Img.findContours(closed2, w, h); console.log(`\n 轮廓数: ${contours.length}`); // Run through filtering logic (matching actual code) let candidateNum = 0; for (const ct of contours) { const area = Img.contourArea(ct); if (area < 100) continue; const brect = Img.boundingRect(ct); const bbArea = brect.w * brect.h; if (area / bbArea < 0.5) continue; const rotatedRect = Img.minAreaRect(ct); const rectArea = rotatedRect.size.w * rotatedRect.size.h; if (area / rectArea < 0.5) continue; const aspectRatio = Math.max(rotatedRect.size.w, rotatedRect.size.h) / Math.max(Math.min(rotatedRect.size.w, rotatedRect.size.h), 1); if (aspectRatio >= 0.9 && aspectRatio <= 1.1) continue; if (aspectRatio > 4.0) continue; const tol = 0.35; const isBig = Math.abs(aspectRatio - 2.0) < tol || Math.abs(aspectRatio - 3.0) < tol; const isSmall = Math.abs(aspectRatio - 1.667) < tol; // Color determination const cx = Math.round(rotatedRect.center.x), cy = Math.round(rotatedRect.center.y); const i3 = (Math.min(h-1, Math.max(0, cy)) * w + Math.min(w-1, Math.max(0, cx))) * 3; const hh = hsv[i3], ss = hsv[i3+1], vv = hsv[i3+2]; let colorLabel = '?'; if (hh >= 180 && hh <= 260) colorLabel = 'BLUE'; else if (hh >= 80 && hh <= 170) colorLabel = 'GREEN'; else if (hh <= 15 || hh >= 340) colorLabel = 'RED'; else colorLabel = `H=${hh.toFixed(0)}`; console.log(` #${candidateNum} area=${area.toFixed(0)} ar=${aspectRatio.toFixed(2)} rect=${(area/rectArea).toFixed(2)} ` + `center=(${cx},${cy}) size=(${rotatedRect.size.w.toFixed(0)},${rotatedRect.size.h.toFixed(0)}) ` + `color=${colorLabel} isBig=${isBig} isSmall=${isSmall} ` + `hsv=(${hh.toFixed(0)},${ss.toFixed(0)},${vv.toFixed(0)})`); candidateNum++; } if (candidateNum === 0) { console.log(` 无候选轮廓! 尝试更宽松的参数...`); // Try lower thresholds const testMasks = [ ['蓝 S20', Img.hsvInRange(hsv, w, h, 180, 260, 20, 255, 20, 255)], ['绿 S20', Img.hsvInRange(hsv, w, h, 80, 170, 20, 255, 20, 255)], ['红 S20', (() => { const m1 = Img.hsvInRange(hsv, w, h, 0, 15, 20, 255, 20, 255); const m2 = Img.hsvInRange(hsv, w, h, 340, 360, 20, 255, 20, 255); const m = new Uint8Array(w*h); for (let i=0;i0) cnt++; console.log(` ${label}: ${cnt} pixels (${(cnt/total*100).toFixed(2)}%)`); } } } async function main() { const args = process.argv.slice(2); const defaultImages = [ '/home/ubuntu/nanocode/web-opencv/cpp/images/11green_1.png', '/home/ubuntu/nanocode/web-opencv/cpp/images/14red_1.png', '/home/ubuntu/nanocode/web-opencv/cpp/images/14blue_1.png', ]; const files = args.length > 0 ? args : defaultImages; for (const f of files) { await diagnose(f); } } main();