feng-x commited on
Commit
a3054b6
·
verified ·
1 Parent(s): 4529e19

Upload folder using huggingface_hub

Browse files
measure_finger.py CHANGED
@@ -31,6 +31,7 @@ from src.confidence import (
31
  compute_overall_confidence,
32
  )
33
  from src.debug_observer import draw_comprehensive_edge_overlay
 
34
 
35
  # Calibration coefficients (from regression on 60 measurements)
36
  _CALIBRATION_PATH = Path(__file__).parent / "src" / "calibration.json"
@@ -703,6 +704,11 @@ def measure_finger(
703
  width_data["median_width_cm"] = cal_cm
704
  width_data["raw_width_cm"] = raw_cm
705
 
 
 
 
 
 
706
  debug_image = draw_comprehensive_edge_overlay(
707
  full_image=image_canonical,
708
  edge_data=edge_data,
@@ -792,6 +798,13 @@ def main() -> int:
792
  else:
793
  result["calibration_applied"] = False
794
 
 
 
 
 
 
 
 
795
  # Save output
796
  save_output(result, args.output)
797
  print(f"Results saved to: {args.output}")
@@ -804,6 +817,9 @@ def main() -> int:
804
  print(f"Finger diameter: {result['finger_outer_diameter_cm']} cm")
805
  if result.get("raw_diameter_cm"):
806
  print(f" (raw: {result['raw_diameter_cm']} cm, calibrated)")
 
 
 
807
  print(f"Confidence: {result['confidence']}")
808
  return 0
809
 
 
31
  compute_overall_confidence,
32
  )
33
  from src.debug_observer import draw_comprehensive_edge_overlay
34
+ from src.ring_size import recommend_ring_size
35
 
36
  # Calibration coefficients (from regression on 60 measurements)
37
  _CALIBRATION_PATH = Path(__file__).parent / "src" / "calibration.json"
 
704
  width_data["median_width_cm"] = cal_cm
705
  width_data["raw_width_cm"] = raw_cm
706
 
707
+ # Add ring size for overlay
708
+ rec = recommend_ring_size(cal_cm)
709
+ if rec:
710
+ width_data["ring_size_rec"] = rec
711
+
712
  debug_image = draw_comprehensive_edge_overlay(
713
  full_image=image_canonical,
714
  edge_data=edge_data,
 
798
  else:
799
  result["calibration_applied"] = False
800
 
801
+ # Ring size recommendation (from calibrated diameter)
802
+ diameter = result.get("finger_outer_diameter_cm")
803
+ if diameter is not None:
804
+ rec = recommend_ring_size(diameter)
805
+ if rec:
806
+ result["ring_size"] = rec
807
+
808
  # Save output
809
  save_output(result, args.output)
810
  print(f"Results saved to: {args.output}")
 
817
  print(f"Finger diameter: {result['finger_outer_diameter_cm']} cm")
818
  if result.get("raw_diameter_cm"):
819
  print(f" (raw: {result['raw_diameter_cm']} cm, calibrated)")
820
+ rec = result.get("ring_size")
821
+ if rec:
822
+ print(f"Ring size: best match {rec['best_match']}, recommended {rec['range_min']}-{rec['range_max']}")
823
  print(f"Confidence: {result['confidence']}")
824
  return 0
825
 
src/debug_observer.py CHANGED
@@ -1203,9 +1203,16 @@ def draw_comprehensive_edge_overlay(
1203
  if raw_cm is not None:
1204
  width_label += f" (raw {raw_cm:.3f})"
1205
 
 
 
 
 
 
 
1206
  annotations = [
1207
  f"Sobel Edge Detection Results:",
1208
  width_label,
 
1209
  f" Std Dev: {std_px:.2f} px",
1210
  f" Valid Edges: {np.sum(valid_rows)}/{len(valid_rows)} ({valid_pct:.1f}%)",
1211
  f" Measurements: {num_samples}",
 
1203
  if raw_cm is not None:
1204
  width_label += f" (raw {raw_cm:.3f})"
1205
 
1206
+ # Ring size recommendation
1207
+ ring_rec = width_data.get("ring_size_rec")
1208
+ ring_label = ""
1209
+ if ring_rec:
1210
+ ring_label = f" Ring Size: best {ring_rec['best_match']}, try {ring_rec['range_min']}-{ring_rec['range_max']}"
1211
+
1212
  annotations = [
1213
  f"Sobel Edge Detection Results:",
1214
  width_label,
1215
+ ring_label,
1216
  f" Std Dev: {std_px:.2f} px",
1217
  f" Valid Edges: {np.sum(valid_rows)}/{len(valid_rows)} ({valid_pct:.1f}%)",
1218
  f" Measurements: {num_samples}",
src/ring_size.py ADDED
@@ -0,0 +1,54 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Ring size recommendation from calibrated finger width."""
2
+
3
+ from typing import Dict, Optional, Tuple
4
+
5
+ # China standard ring size chart: size → inner diameter (mm)
6
+ RING_SIZE_CHART = {
7
+ 6: 16.9,
8
+ 7: 17.7,
9
+ 8: 18.6,
10
+ 9: 19.4,
11
+ 10: 20.3,
12
+ 11: 21.1,
13
+ 12: 21.9,
14
+ 13: 22.7,
15
+ }
16
+
17
+ # Sorted sizes for lookup
18
+ _SORTED_SIZES = sorted(RING_SIZE_CHART.items(), key=lambda x: x[1])
19
+
20
+
21
+ def recommend_ring_size(diameter_cm: float) -> Optional[Dict]:
22
+ """Recommend ring size from calibrated finger outer diameter.
23
+
24
+ Returns dict with:
25
+ - best_match: nearest ring size (int)
26
+ - best_match_inner_mm: inner diameter of best match
27
+ - range_min / range_max: recommended 2-size range
28
+ - diameter_mm: input converted to mm
29
+ Returns None if diameter is out of reasonable range.
30
+ """
31
+ diameter_mm = diameter_cm * 10.0
32
+
33
+ if diameter_mm < 14.0 or diameter_mm > 26.0:
34
+ return None
35
+
36
+ # Find nearest size
37
+ best_size, best_inner = min(_SORTED_SIZES, key=lambda x: abs(x[1] - diameter_mm))
38
+
39
+ # Find second nearest size
40
+ second_size, second_inner = min(
41
+ (s for s in _SORTED_SIZES if s[0] != best_size),
42
+ key=lambda x: abs(x[1] - diameter_mm),
43
+ )
44
+
45
+ range_min = min(best_size, second_size)
46
+ range_max = max(best_size, second_size)
47
+
48
+ return {
49
+ "best_match": best_size,
50
+ "best_match_inner_mm": best_inner,
51
+ "range_min": range_min,
52
+ "range_max": range_max,
53
+ "diameter_mm": round(diameter_mm, 2),
54
+ }
web_demo/app.py CHANGED
@@ -20,6 +20,7 @@ ROOT_DIR = Path(__file__).resolve().parents[1]
20
  sys.path.insert(0, str(ROOT_DIR))
21
 
22
  from measure_finger import measure_finger, apply_calibration
 
23
 
24
  APP_ROOT = Path(__file__).resolve().parent
25
  UPLOAD_DIR = APP_ROOT / "uploads"
@@ -127,8 +128,12 @@ def _run_measurement(
127
  raw_diameter = result.get("finger_outer_diameter_cm")
128
  if raw_diameter is not None:
129
  result["raw_diameter_cm"] = round(raw_diameter, 4)
130
- result["finger_outer_diameter_cm"] = round(apply_calibration(raw_diameter), 4)
 
131
  result["calibration_applied"] = True
 
 
 
132
 
133
  result_json_name = f"{run_id}__result.json"
134
  result_json_path = RESULTS_DIR / result_json_name
 
20
  sys.path.insert(0, str(ROOT_DIR))
21
 
22
  from measure_finger import measure_finger, apply_calibration
23
+ from src.ring_size import recommend_ring_size
24
 
25
  APP_ROOT = Path(__file__).resolve().parent
26
  UPLOAD_DIR = APP_ROOT / "uploads"
 
128
  raw_diameter = result.get("finger_outer_diameter_cm")
129
  if raw_diameter is not None:
130
  result["raw_diameter_cm"] = round(raw_diameter, 4)
131
+ calibrated = round(apply_calibration(raw_diameter), 4)
132
+ result["finger_outer_diameter_cm"] = calibrated
133
  result["calibration_applied"] = True
134
+ rec = recommend_ring_size(calibrated)
135
+ if rec:
136
+ result["ring_size"] = rec
137
 
138
  result_json_name = f"{run_id}__result.json"
139
  result_json_path = RESULTS_DIR / result_json_name