import sharp from 'sharp'; import * as fs from 'fs'; import * as path from 'path'; async function debugSingle(imagePath: string) { const basename = path.basename(imagePath); console.log(`\n=== ${basename} ===`); const { data, info } = await sharp(imagePath) .raw() .toBuffer({ resolveWithObject: true }); const { width, height, channels } = info; const darkThreshold = 85; const darkMap = new Uint8Array(width * height); for (let y = 0; y < height; y++) { for (let x = 0; x < width; x++) { const idx = (y * width + x) * channels; const r = data[idx]; const g = data[idx + 1]; const b = data[idx + 2]; const brightness = (r * 0.299 + g * 0.587 + b * 0.114); darkMap[y * width + x] = brightness < darkThreshold ? 1 : 0; } } const visited = new Uint8Array(width * height); const regions: Array<{x: number; y: number; w: number; h: number; pixels: number}> = []; for (let y = 0; y < height; y++) { for (let x = 0; x < width; x++) { const idx = y * width + x; if (visited[idx] === 0 && darkMap[idx] === 1) { let minX = x, minY = y, maxX = x, maxY = y, pixelCount = 0; const stack: Array<[number, number]> = [[x, y]]; while (stack.length > 0) { const [cx, cy] = stack.pop()!; if (cx < 0 || cx >= width || cy < 0 || cy >= height) continue; const cidx = cy * width + cx; if (visited[cidx] === 1 || darkMap[cidx] !== 1) continue; visited[cidx] = 1; pixelCount++; minX = Math.min(minX, cx); minY = Math.min(minY, cy); maxX = Math.max(maxX, cx); maxY = Math.max(maxY, cy); stack.push([cx + 1, cy], [cx - 1, cy], [cx, cy + 1], [cx, cy - 1]); } const w = maxX - minX + 1; const h = maxY - minY + 1; if (w >= 20 && h >= 20 && w < width * 0.9 && h < height * 0.9) { regions.push({x: minX, y: minY, w, h, pixels: pixelCount}); } } } } console.log(`找到 ${regions.length} 个有效连通区域`); // 过滤符合条件的候选 const candidates = regions.filter(r => { const aspectRatio = r.w / r.h; const density = r.pixels / (r.w * r.h); const centerY = r.y + r.h / 2; return ( r.w >= 50 && r.w <= 95 && r.h >= 50 && r.h <= 95 && aspectRatio >= 0.85 && aspectRatio <= 1.18 && centerY > height * 0.12 && centerY < height * 0.78 && density > 0.65 ); }); console.log(`符合严格条件的候选: ${candidates.length} 个`); if (candidates.length > 0) { candidates.forEach((r, i) => { const aspectRatio = r.w / r.h; const density = r.pixels / (r.w * r.h); console.log(` ${i + 1}. [x=${r.x}, y=${r.y}, w=${r.w}, h=${r.h}] 宽高比=${aspectRatio.toFixed(2)}, 密度=${density.toFixed(2)}`); }); } else { // 尝试放宽条件 const relaxed = regions.filter(r => { const aspectRatio = r.w / r.h; const density = r.pixels / (r.w * r.h); return ( r.w >= 45 && r.w <= 100 && r.h >= 45 && r.h <= 100 && aspectRatio >= 0.75 && aspectRatio <= 1.33 && r.y < height * 0.82 && r.y > height * 0.06 && density > 0.55 ); }); console.log(`符合放宽条件的候选: ${relaxed.length} 个`); relaxed.slice(0, 5).forEach((r, i) => { const aspectRatio = r.w / r.h; const density = r.pixels / (r.w * r.h); console.log(` ${i + 1}. [x=${r.x}, y=${r.y}, w=${r.w}, h=${r.h}] 宽高比=${aspectRatio.toFixed(2)}, 密度=${density.toFixed(2)}`); }); } } async function main() { const baseDir = path.join(__dirname, '..'); const doubanDir = path.join(baseDir, 'images', 'douban'); // 检查未检测到的图片 const failedFiles = ['滑块-2.png', '滑块-3.png', '滑块-6.png', '滑块-7.png', '滑块.png']; for (const file of failedFiles) { await debugSingle(path.join(doubanDir, file)); } } main().catch(console.error);