CoolFace
Apppublic

RuslanKain/sorting-searching-recognized-gestures

sourceHugging Facemitupdated 6mo agoView on Hugging Face
0likes
scoring.py217 linesDownload Raw Back to models
1"""2╔══════════════════════════════════════════════════════════════════════════════╗3║  Models: scoring.py                                                          ║4║  Scoring system for the Gesture Confidence Ranker                            ║5╚══════════════════════════════════════════════════════════════════════════════╝6 7This module contains:8• GestureScorer - Computes quality scores for recognized gestures9 10📚 WHY A SEPARATE SCORING CLASS?11 12   In procedural code, you'd write a function mixed in with everything else.13   With OOP, we create a dedicated class that:14   15   1. ENCAPSULATES the scoring formula and its parameters16   2. Has a clear interface (apply_scores, clear_scores, compute_score)17   3. Is easy to test independently18   4. Can be extended with new formulas without changing existing code19 20💡 DESIGN PATTERN: Single Responsibility Principle21 22   GestureScorer has ONE job: compute quality scores.23   - It does NOT sort (that's the algorithm's job).24   - It does NOT display (that's the renderer's job).25   - It does NOT manage the list (that's ImageList's job).26   27   Each class does one thing well!28"""29 30from typing import List31 32from .gesture import GestureImage, GestureRanking33 34 35# ==============================================================================36# CLASS: GestureScorer37# ==============================================================================38 39class GestureScorer:40    """41    Computes quality scores for gesture recognition results.42    43    The quality score measures how well an AI model recognized a gesture,44    taking into account both confidence and gesture complexity.45    46    ┌─────────────────────────────────────────────────────────────────────────┐47    │  📊 THE FORMULA                                                         │48    │                                                                         │49    │  quality_score = confidence × 100 × complexity ^ complexity_weight      │50    │                  × confidence_weight                                    │51    │                                                                         │52    │  Where:                                                                 │53    │  • confidence (0.0–1.0): How sure the AI was about its prediction      │54    │  • complexity (1.0–2.0): How hard the gesture is to recognize           │55    │  • confidence_weight: User-tunable importance of confidence             │56    │  • complexity_weight: User-tunable importance of complexity             │57    │                                                                         │58    │  Example:                                                               │59    │    ✊ fist with 90% confidence:  0.90 × 100 × 1.0¹·⁰ × 1.0 = 90.0    │60    │    👌 ok with 80% confidence:   0.80 × 100 × 1.7¹·⁰ × 1.0 = 136.0   │61    │    → OK sign ranks HIGHER because recognizing it is harder!            │62    │                                                                         │63    │  💡 This is like the Scholarship Shortlist problem (#4):               │64    │     Instead of GPA + volunteer_hours + essay_score,                     │65    │     we use confidence × complexity to produce a final score.            │66    └─────────────────────────────────────────────────────────────────────────┘67    68    ┌─────────────────────────────────────────────────────────────────────────┐69    │  📚 CONCEPT: Class Methods vs Static Methods vs Instance Methods        │70    │                                                                         │71    │  This class uses @classmethod (no instance needed):                     │72    │      GestureScorer.compute_score(gesture)  # Called on the CLASS        │73    │                                                                         │74    │  vs. Instance method (needs an object):                                │75    │      scorer = GestureScorer()                                           │76    │      scorer.compute_score(gesture)          # Called on an OBJECT       │77    │                                                                         │78    │  We use @classmethod because GestureScorer doesn't need to store       │79    │  any per-instance data. It's a utility class with shared logic.        │80    └─────────────────────────────────────────────────────────────────────────┘81    """82    83    # -------------------------------------------------------------------------84    # Class Constants: Default weights for the scoring formula85    # -------------------------------------------------------------------------86    DEFAULT_CONFIDENCE_WEIGHT = 1.087    DEFAULT_COMPLEXITY_WEIGHT = 1.088    89    # -------------------------------------------------------------------------90    # Scoring Methods91    # -------------------------------------------------------------------------92    93    @classmethod94    def compute_score(95        cls,96        gesture: GestureImage,97        confidence_weight: float = DEFAULT_CONFIDENCE_WEIGHT,98        complexity_weight: float = DEFAULT_COMPLEXITY_WEIGHT,99    ) -> float:100        """101        Compute the quality score for a single gesture.102        103        Args:104            gesture: The GestureImage to score105            confidence_weight: How much to weight confidence (default 1.0)106            complexity_weight: How much to weight complexity (default 1.0)107            108        Returns:109            The computed quality score (higher = better recognition)110            111        Example:112            >>> fist = GestureImage.create_from_prediction("fist", 1, confidence=0.9)113            >>> GestureScorer.compute_score(fist)114            90.0115        """116        complexity = GestureRanking.get_complexity(gesture.gesture)117        score = (118            gesture.confidence119            * 100120            * (complexity ** complexity_weight)121            * confidence_weight122        )123        return round(score, 2)124    125    @classmethod126    def apply_scores(127        cls,128        images: List[GestureImage],129        confidence_weight: float = DEFAULT_CONFIDENCE_WEIGHT,130        complexity_weight: float = DEFAULT_COMPLEXITY_WEIGHT,131    ) -> None:132        """133        Compute and apply quality scores to a list of gestures.134        135        This sets each gesture's _sort_value to the NEGATED score,136        so that sorting in ascending order produces DESCENDING score order.137        (Highest quality score appears first!)138        139        ┌─────────────────────────────────────────────────────────────────────┐140        │  💡 WHY NEGATE THE SCORE?                                           │141        │                                                                     │142        │  Our sorting algorithms sort in ASCENDING order (small → large).   │143        │  But we want the HIGHEST score at the top of the ranked list.      │144        │                                                                     │145        │  Solution: negate the score before sorting!                        │146        │    Score 150.0 → sort_value = -150.0  (comes FIRST in ascending)   │147        │    Score  90.0 → sort_value =  -90.0  (comes SECOND)              │148        │    Score  50.0 → sort_value =  -50.0  (comes LAST)                │149        │                                                                     │150        │  This way, the existing algorithms work correctly without          │151        │  needing any modifications. Open/Closed Principle in action!       │152        └─────────────────────────────────────────────────────────────────────┘153        154        Args:155            images: List of GestureImage objects to score156            confidence_weight: How much to weight confidence157            complexity_weight: How much to weight complexity158        """159        for gesture in images:160            score = cls.compute_score(gesture, confidence_weight, complexity_weight)161            # Negate so ascending sort → descending score order162            gesture._sort_value = -score163    164    @classmethod165    def clear_scores(cls, images: List[GestureImage]) -> None:166        """167        Remove custom scores from all gestures, reverting to rank-based sorting.168        169        After calling this, the gestures will sort by their original170        gesture rank (fist < peace < ok < call, etc.).171        172        Args:173            images: List of GestureImage objects to clear scores from174        """175        for gesture in images:176            gesture._sort_value = None177    178    @classmethod179    def get_score(cls, gesture: GestureImage) -> float:180        """181        Get the positive (non-negated) score for display purposes.182        183        The _sort_value is stored as a negative number (for descending sort),184        but users should see the positive quality score.185        186        Args:187            gesture: The GestureImage to get the display score for188            189        Returns:190            The positive quality score, or 0.0 if no score is set191        """192        if gesture._sort_value is not None:193            return -gesture._sort_value  # Negate back to positive194        return 0.0195    196    @classmethod197    def get_formula_description(198        cls,199        confidence_weight: float = DEFAULT_CONFIDENCE_WEIGHT,200        complexity_weight: float = DEFAULT_COMPLEXITY_WEIGHT,201    ) -> str:202        """203        Get a human-readable description of the current scoring formula.204        205        Returns:206            A string explaining the formula with the current weight values.207            208        Example:209            >>> GestureScorer.get_formula_description(1.0, 1.5)210            'quality = confidence × 100 × complexity^1.5 × 1.0'211        """212        return (213            f"quality = confidence × 100 "214            f"× complexity^{complexity_weight:.1f} "215            f"× {confidence_weight:.1f}"216        )217