Files
Ubuntu 32b43f85bd a
2026-07-03 16:58:28 +08:00

202 lines
7.6 KiB
JavaScript
Raw Permalink 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.
/**
* 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;i<w*h;i++) m[i]=Math.max(m1[i],m2[i]);
return m;
})()]
];
for (const [label, mask] of testMasks) {
let cnt = 0;
for (let i=0;i<w*h;i++) if(mask[i]>0) 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();