This commit is contained in:
Ubuntu
2026-07-03 16:58:28 +08:00
commit 32b43f85bd
6240 changed files with 1987678 additions and 0 deletions
+201
View File
@@ -0,0 +1,201 @@
/**
* 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();