Spaces:
Running
Running
Upload folder using huggingface_hub
Browse files- measure_finger.py +20 -13
- src/visualization.py +14 -2
- web_demo/static/app.js +3 -2
- web_demo/static/examples/default_sample.jpg +2 -2
- web_demo/templates/index.html +6 -5
measure_finger.py
CHANGED
|
@@ -40,6 +40,7 @@ from src.confidence import (
|
|
| 40 |
compute_overall_confidence,
|
| 41 |
)
|
| 42 |
from src.debug_observer import draw_comprehensive_edge_overlay, draw_hand_skeleton
|
|
|
|
| 43 |
from src.ring_size import recommend_ring_size, aggregate_ring_sizes, VALID_RING_MODELS, DEFAULT_RING_MODEL
|
| 44 |
from src.image_quality import (
|
| 45 |
check_card_in_frame,
|
|
@@ -997,10 +998,14 @@ def measure_finger(
|
|
| 997 |
# Tint SAM hand + card masks as underlays. Both masks live in the
|
| 998 |
# pre-precise-rotation canonical frame, so apply the same rotation
|
| 999 |
# matrix that was used to align the finger.
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1000 |
debug_image = _overlay_sam_masks(
|
| 1001 |
debug_image,
|
| 1002 |
hand_mask=hand_data.get("mask") if hand_data else None,
|
| 1003 |
-
card_mask=
|
| 1004 |
rotation_matrix=rotation_matrix,
|
| 1005 |
)
|
| 1006 |
|
|
@@ -1013,16 +1018,16 @@ def measure_finger(
|
|
| 1013 |
rotation_matrix=rotation_matrix,
|
| 1014 |
)
|
| 1015 |
|
| 1016 |
-
#
|
| 1017 |
-
if card_result is not None and "corners"
|
| 1018 |
-
|
| 1019 |
-
if
|
| 1020 |
-
pts =
|
| 1021 |
-
|
| 1022 |
-
|
| 1023 |
-
|
| 1024 |
-
|
| 1025 |
-
|
| 1026 |
|
| 1027 |
# Save result image (downscaled + JPEG-encoded for speed)
|
| 1028 |
_save_debug_visualization(result_png_path, debug_image)
|
|
@@ -1468,11 +1473,13 @@ def _draw_multi_finger_debug(
|
|
| 1468 |
vis = image_canonical.copy()
|
| 1469 |
h, w = vis.shape[:2]
|
| 1470 |
|
| 1471 |
-
# SAM
|
|
|
|
|
|
|
| 1472 |
vis = _overlay_sam_masks(
|
| 1473 |
vis,
|
| 1474 |
hand_mask=hand_mask,
|
| 1475 |
-
card_mask=
|
| 1476 |
)
|
| 1477 |
|
| 1478 |
# MediaPipe hand skeleton (canonical frame — no rotation needed since the
|
|
|
|
| 40 |
compute_overall_confidence,
|
| 41 |
)
|
| 42 |
from src.debug_observer import draw_comprehensive_edge_overlay, draw_hand_skeleton
|
| 43 |
+
from src.visualization import draw_card_overlay
|
| 44 |
from src.ring_size import recommend_ring_size, aggregate_ring_sizes, VALID_RING_MODELS, DEFAULT_RING_MODEL
|
| 45 |
from src.image_quality import (
|
| 46 |
check_card_in_frame,
|
|
|
|
| 998 |
# Tint SAM hand + card masks as underlays. Both masks live in the
|
| 999 |
# pre-precise-rotation canonical frame, so apply the same rotation
|
| 1000 |
# matrix that was used to align the finger.
|
| 1001 |
+
# Only the hand SAM mask gets tinted as an underlay. The card SAM
|
| 1002 |
+
# mask often has stray blobs outside the actual card rectangle; the
|
| 1003 |
+
# detected corners already give us a clean quadrilateral, so we
|
| 1004 |
+
# shade that via draw_card_overlay below instead.
|
| 1005 |
debug_image = _overlay_sam_masks(
|
| 1006 |
debug_image,
|
| 1007 |
hand_mask=hand_data.get("mask") if hand_data else None,
|
| 1008 |
+
card_mask=None,
|
| 1009 |
rotation_matrix=rotation_matrix,
|
| 1010 |
)
|
| 1011 |
|
|
|
|
| 1018 |
rotation_matrix=rotation_matrix,
|
| 1019 |
)
|
| 1020 |
|
| 1021 |
+
# Shade + outline the card using its detected corner rectangle.
|
| 1022 |
+
if card_result is not None and card_result.get("corners") is not None:
|
| 1023 |
+
pts = np.asarray(card_result["corners"], dtype=np.float32)
|
| 1024 |
+
if rotation_matrix is not None:
|
| 1025 |
+
pts = transform_points_rotation(pts, rotation_matrix)
|
| 1026 |
+
draw_card_overlay(
|
| 1027 |
+
debug_image,
|
| 1028 |
+
{"corners": pts},
|
| 1029 |
+
scale_px_per_cm=px_per_cm,
|
| 1030 |
+
)
|
| 1031 |
|
| 1032 |
# Save result image (downscaled + JPEG-encoded for speed)
|
| 1033 |
_save_debug_visualization(result_png_path, debug_image)
|
|
|
|
| 1473 |
vis = image_canonical.copy()
|
| 1474 |
h, w = vis.shape[:2]
|
| 1475 |
|
| 1476 |
+
# Hand SAM silhouette as a tinted underlay. The card is shaded via its
|
| 1477 |
+
# clean corner rectangle in draw_card_overlay() below rather than the
|
| 1478 |
+
# raw SAM mask (which often has stray blobs outside the card boundary).
|
| 1479 |
vis = _overlay_sam_masks(
|
| 1480 |
vis,
|
| 1481 |
hand_mask=hand_mask,
|
| 1482 |
+
card_mask=None,
|
| 1483 |
)
|
| 1484 |
|
| 1485 |
# MediaPipe hand skeleton (canonical frame — no rotation needed since the
|
src/visualization.py
CHANGED
|
@@ -129,10 +129,22 @@ def draw_card_overlay(
|
|
| 129 |
card_result: Dict[str, Any],
|
| 130 |
scale_px_per_cm: Optional[float] = None,
|
| 131 |
) -> np.ndarray:
|
| 132 |
-
"""Draw credit card detection overlay.
|
| 133 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 134 |
params = get_scaled_font_params(image.shape[0])
|
| 135 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 136 |
# Draw quadrilateral
|
| 137 |
cv2.polylines(image, [corners], isClosed=True, color=Color.CARD,
|
| 138 |
thickness=params["contour_thickness"])
|
|
|
|
| 129 |
card_result: Dict[str, Any],
|
| 130 |
scale_px_per_cm: Optional[float] = None,
|
| 131 |
) -> np.ndarray:
|
| 132 |
+
"""Draw credit card detection overlay.
|
| 133 |
+
|
| 134 |
+
The card region is highlighted by shading just the detected corner
|
| 135 |
+
rectangle (not the raw SAM mask, which can have noisy overspill). A
|
| 136 |
+
solid outline with TL/TR/BR/BL corner labels is drawn on top.
|
| 137 |
+
"""
|
| 138 |
+
corners = np.asarray(card_result["corners"], dtype=np.int32)
|
| 139 |
params = get_scaled_font_params(image.shape[0])
|
| 140 |
|
| 141 |
+
# Semi-transparent fill inside the corner quadrilateral. Avoids the
|
| 142 |
+
# visual clutter of drawing the raw SAM mask (which often has stray
|
| 143 |
+
# blobs outside the real card boundary).
|
| 144 |
+
tint = np.zeros_like(image)
|
| 145 |
+
cv2.fillConvexPoly(tint, corners, Color.CARD)
|
| 146 |
+
cv2.addWeighted(image, 1.0, tint, 0.22, 0, dst=image)
|
| 147 |
+
|
| 148 |
# Draw quadrilateral
|
| 149 |
cv2.polylines(image, [corners], isClosed=True, color=Color.CARD,
|
| 150 |
thickness=params["contour_thickness"])
|
web_demo/static/app.js
CHANGED
|
@@ -108,8 +108,9 @@ const buildMeasureSettings = () => {
|
|
| 108 |
const aiToggle = document.getElementById("aiExplainToggle");
|
| 109 |
const mode = modeSelect ? modeSelect.value : "multi";
|
| 110 |
const ringModel = ringModelSelect ? ringModelSelect.value : "gen";
|
| 111 |
-
//
|
| 112 |
-
|
|
|
|
| 113 |
return {
|
| 114 |
finger_index: fingerSelect ? fingerSelect.value : "index",
|
| 115 |
edge_method: "mask",
|
|
|
|
| 108 |
const aiToggle = document.getElementById("aiExplainToggle");
|
| 109 |
const mode = modeSelect ? modeSelect.value : "multi";
|
| 110 |
const ringModel = ringModelSelect ? ringModelSelect.value : "gen";
|
| 111 |
+
// AI explanation is off by default. Dev mode shows a checkbox the user
|
| 112 |
+
// can opt into; non-dev mode has no UI control and therefore stays off.
|
| 113 |
+
const aiOn = aiToggle && aiToggle.type === "checkbox" ? aiToggle.checked : false;
|
| 114 |
return {
|
| 115 |
finger_index: fingerSelect ? fingerSelect.value : "index",
|
| 116 |
edge_method: "mask",
|
web_demo/static/examples/default_sample.jpg
CHANGED
|
Git LFS Details
|
|
Git LFS Details
|
web_demo/templates/index.html
CHANGED
|
@@ -26,11 +26,12 @@
|
|
| 26 |
<span class="file-hint">JPG / PNG supported · 1080p or higher recommended</span>
|
| 27 |
</label>
|
| 28 |
|
| 29 |
-
<ul class="capture-tips"
|
| 30 |
-
<li><strong>Turn on your phone's flash</strong>, it
|
|
|
|
| 31 |
<li><strong>Use plain white background</strong>, a sheet of paper works great.</li>
|
| 32 |
-
<li><strong>Spread your fingers naturally</strong>
|
| 33 |
-
<li>
|
| 34 |
</ul>
|
| 35 |
|
| 36 |
<div class="controls">
|
|
@@ -66,7 +67,7 @@
|
|
| 66 |
<label class="toggle-label">
|
| 67 |
<span>AI Explanation</span>
|
| 68 |
<div class="toggle-row">
|
| 69 |
-
<input type="checkbox" id="aiExplainToggle"
|
| 70 |
<span class="toggle-hint">Uses OpenAI tokens</span>
|
| 71 |
</div>
|
| 72 |
</label>
|
|
|
|
| 26 |
<span class="file-hint">JPG / PNG supported · 1080p or higher recommended</span>
|
| 27 |
</label>
|
| 28 |
|
| 29 |
+
<ul class="capture-tips">
|
| 30 |
+
<li><strong>Turn on your phone's flash</strong>, it sharpens finger edges.</li>
|
| 31 |
+
<li>Hold phone <strong>directly above</strong> hand, <strong>parallel</strong> to table.</li>
|
| 32 |
<li><strong>Use plain white background</strong>, a sheet of paper works great.</li>
|
| 33 |
+
<li><strong>Spread your fingers naturally</strong>.</li>
|
| 34 |
+
<li>Place the card <strong>beside your hand</strong>, fully visible.</li>
|
| 35 |
</ul>
|
| 36 |
|
| 37 |
<div class="controls">
|
|
|
|
| 67 |
<label class="toggle-label">
|
| 68 |
<span>AI Explanation</span>
|
| 69 |
<div class="toggle-row">
|
| 70 |
+
<input type="checkbox" id="aiExplainToggle" />
|
| 71 |
<span class="toggle-hint">Uses OpenAI tokens</span>
|
| 72 |
</div>
|
| 73 |
</label>
|