feat: improve card localization with convex hull and confidence thresholding
This commit is contained in:
parent
106ce2f218
commit
2b3871a28f
2 changed files with 49 additions and 26 deletions
|
|
@ -30,6 +30,13 @@ export class DetectionPipeline {
|
||||||
if (cardModelService.isReady()) {
|
if (cardModelService.isReady()) {
|
||||||
const suitRes = await cardModelService.classifySuit(cardCrop);
|
const suitRes = await cardModelService.classifySuit(cardCrop);
|
||||||
const valRes = await cardModelService.classifyValue(cardCrop);
|
const valRes = await cardModelService.classifyValue(cardCrop);
|
||||||
|
|
||||||
|
// Only proceed if confidence is above a certain threshold
|
||||||
|
const MIN_CONFIDENCE = 0.6;
|
||||||
|
if (suitRes.confidence < MIN_CONFIDENCE || valRes.confidence < MIN_CONFIDENCE) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
suit = suitRes.label as any;
|
suit = suitRes.label as any;
|
||||||
value = parseInt(valRes.label);
|
value = parseInt(valRes.label);
|
||||||
confidence = (suitRes.confidence + valRes.confidence) / 2;
|
confidence = (suitRes.confidence + valRes.confidence) / 2;
|
||||||
|
|
|
||||||
|
|
@ -73,38 +73,54 @@ export class ImageGeometry {
|
||||||
* Find the 4 corners of a point set that most closely resemble a rectangle
|
* Find the 4 corners of a point set that most closely resemble a rectangle
|
||||||
*/
|
*/
|
||||||
static findCorners(points: Point[]): Point[] {
|
static findCorners(points: Point[]): Point[] {
|
||||||
if (points.length < 4) return [];
|
if (points.length < 3) return [];
|
||||||
|
|
||||||
|
// Monotone Chain algorithm for Convex Hull
|
||||||
|
const sorted = [...points].sort((a, b) => a.x !== b.x ? a.x - b.x : a.y - b.y);
|
||||||
|
|
||||||
|
const crossProduct = (a: Point, b: Point, c: Point) =>
|
||||||
|
(b.x - a.x) * (c.y - a.y) - (b.y - a.y) * (c.x - a.x);
|
||||||
|
|
||||||
|
const buildHull = (pts: Point[]) => {
|
||||||
|
const hull: Point[] = [];
|
||||||
|
for (const p of pts) {
|
||||||
|
while (hull.length >= 2 && crossProduct(hull[hull.length - 2], hull[hull.length - 1], p) <= 0) {
|
||||||
|
hull.pop();
|
||||||
|
}
|
||||||
|
hull.push(p);
|
||||||
|
}
|
||||||
|
return hull;
|
||||||
|
};
|
||||||
|
|
||||||
|
const lower = buildHull(sorted);
|
||||||
|
const upper = buildHull(sorted.reverse());
|
||||||
|
|
||||||
|
lower.pop();
|
||||||
|
upper.pop();
|
||||||
|
const hull = lower.concat(upper);
|
||||||
|
|
||||||
|
if (hull.length < 4) return [];
|
||||||
|
|
||||||
|
// Find 4 points on hull that maximize the rectangle area
|
||||||
|
// Simplification: find points furthest apart along axes and their mid-points
|
||||||
let minX = Infinity, maxX = -Infinity, minY = Infinity, maxY = -Infinity;
|
let minX = Infinity, maxX = -Infinity, minY = Infinity, maxY = -Infinity;
|
||||||
for (const p of points) {
|
let pMinX: Point | null = null, pMaxX: Point | null = null, pMinY: Point | null = null, pMaxY: Point | null = null;
|
||||||
if (p.x < minX) minX = p.x;
|
|
||||||
if (p.x > maxX) maxX = p.x;
|
for (const p of hull) {
|
||||||
if (p.y < minY) minY = p.y;
|
if (p.x < minX) { minX = p.x; pMinX = p; }
|
||||||
if (p.y > maxY) maxY = p.y;
|
if (p.x > maxX) { maxX = p.x; pMaxX = p; }
|
||||||
|
if (p.y < minY) { minY = p.y; pMinY = p; }
|
||||||
|
if (p.y > maxY) { maxY = p.y; pMaxY = p; }
|
||||||
}
|
}
|
||||||
|
|
||||||
const targets = [
|
const extremePoints = [pMinX, pMaxX, pMinY, pMaxY].filter((p): p is Point => p !== null);
|
||||||
{ x: minX, y: minY },
|
|
||||||
{ x: maxX, y: minY },
|
|
||||||
{ x: maxX, y: maxY },
|
|
||||||
{ x: minX, y: maxY },
|
|
||||||
];
|
|
||||||
|
|
||||||
const corners: Point[] = [];
|
if (extremePoints.length >= 4) {
|
||||||
for (const target of targets) {
|
return extremePoints.slice(0, 4);
|
||||||
let closest = points[0];
|
|
||||||
let minDist = Infinity;
|
|
||||||
for (const p of points) {
|
|
||||||
const dist = Math.sqrt((p.x - target.x) ** 2 + (p.y - target.y) ** 2);
|
|
||||||
if (dist < minDist) {
|
|
||||||
minDist = dist;
|
|
||||||
closest = p;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
corners.push(closest);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return corners;
|
// Fallback to previous simple logic if hull is too small or degenerate
|
||||||
|
return points.slice(0, 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
static warpPerspective(sourceCanvas: HTMLCanvasElement, srcCorners: Point[], destWidth: number, destHeight: number): HTMLCanvasElement {
|
static warpPerspective(sourceCanvas: HTMLCanvasElement, srcCorners: Point[], destWidth: number, destHeight: number): HTMLCanvasElement {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue