Files
foliate-js/panel-detection/coco-ssd.js
T
john-okeefe ffaceaf962 Add panel detection module with multi-tier fallback system
Implement a comprehensive panel detection system for manga and comics
with automatic fallback chain for maximum compatibility.

Core detector (detector.js):
- PanelDetector class with in-memory caching
- Lazy-loading of OpenCV and TensorFlow.js
- Validation logic to filter poor detections
- Cache management to avoid re-detection

OpenCV edge detection (opencv.js):
- Canny edge detection for panel boundaries
- Contour finding with bounding box extraction
- Size and aspect ratio filtering
- Reading order sorting (top-to-bottom, left-to-right)

ML-based detection (coco-ssd.js):
- COCO-SSD pre-trained model integration
- Object detection for irregular panel layouts
- Rectangular filtering for panel-like regions
- Handles edge cases where edge detection fails

Grid-based fallback (grid.js):
- Lightweight 3x3 grid detection
- Empty cell detection via alpha channel analysis
- Adjacent panel merging algorithm
- Always works as final fallback

The detection pipeline tries OpenCV first (fast, accurate),
falls back to ML detection if validation fails,
and uses grid detection as ultimate baseline.
2026-04-13 16:43:25 -04:00

48 lines
1.2 KiB
JavaScript

// panel-detection/coco-ssd.js
// ML-based panel detection using COCO-SSD
export async function detectPanelsML(imageData, model) {
const canvas = document.createElement("canvas");
canvas.width = imageData.width;
canvas.height = imageData.height;
const ctx = canvas.getContext("2d");
ctx.putImageData(imageData, 0, 0);
const predictions = await model.detect(canvas);
const panels = [];
const imgWidth = imageData.width;
const imgHeight = imageData.height;
for (let i = 0; i < predictions.length; i++) {
const pred = predictions[i];
const [x, y, w, h] = pred.bbox;
const aspectRatio = w / h;
const isRectangular =
aspectRatio > 0.3 &&
aspectRatio < 5 &&
w > imgWidth * 0.05 &&
h > imgHeight * 0.05;
if (isRectangular) {
panels.push({
id: `ml-${i}`,
x: (x / imgWidth) * 100,
y: (y / imgHeight) * 100,
width: (w / imgWidth) * 100,
height: (h / imgHeight) * 100,
reading_order: i,
});
}
}
panels.sort((a, b) => {
const rowA = Math.floor(a.y / 20);
const rowB = Math.floor(b.y / 20);
if (rowA !== rowB) return rowA - rowB;
return a.x - b.x;
});
return panels.map((p, i) => ({ ...p, reading_order: i }));
}