retuve.hip_us.metrics.curvature

Metric: Curvature

An experimental metric that calculates the "curvature" of the hip.

Overtime, there will be different versions of this metric as it is still being developed. Which version will be controlled from the config.

  1# Copyright 2024 Adam McArthur
  2#
  3# Licensed under the Apache License, Version 2.0 (the "License");
  4# you may not use this file except in compliance with the License.
  5# You may obtain a copy of the License at
  6#
  7#     http://www.apache.org/licenses/LICENSE-2.0
  8#
  9# Unless required by applicable law or agreed to in writing, software
 10# distributed under the License is distributed on an "AS IS" BASIS,
 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 12# See the License for the specific language governing permissions and
 13# limitations under the License.
 14
 15"""
 16Metric: Curvature
 17
 18An experimental metric that calculates the "curvature" of the hip.
 19
 20Overtime, there will be different versions of this metric as it is
 21still being developed. Which version will be controlled from the config.
 22"""
 23
 24import math
 25from typing import Tuple
 26
 27import numpy as np
 28
 29from retuve.classes.draw import Overlay
 30from retuve.hip_us.classes.general import HipDataUS, LandmarksUS
 31from retuve.keyphrases.config import Config
 32from retuve.keyphrases.enums import Curvature, MetricUS
 33
 34
 35def find_curvature(
 36    landmarks: LandmarksUS, shape: Tuple, config: Config
 37) -> float:
 38    """
 39    Calculate the curvature of the hip.
 40
 41    :param landmarks: LandmarksUS: The landmarks object.
 42    :param shape: Tuple: The shape of the image.
 43    :param config: Config: The Config object.
 44
 45    :return: float: The curvature of the hip.
 46    """
 47    if not (
 48        landmarks and landmarks.left and landmarks.apex and landmarks.right
 49    ):
 50        return 0
 51
 52    if config.hip.curvature_method == Curvature.RADIST:
 53        width = shape[1]
 54
 55        # This normalisation factor can handle bad crmodes,
 56        # As well as off-center scans
 57        normalise_factor = abs((width // 2) - landmarks.left[0])
 58
 59        total_distance = 0
 60
 61        for a, b in [
 62            (landmarks.left, landmarks.apex),
 63            (landmarks.right, landmarks.apex),
 64        ]:
 65            # Get the distance between two points in 2D space
 66            distance_x = abs(a[0] - b[0])
 67            distance_y = abs(a[1] - b[1])
 68
 69            # Calculate the distance between the two points
 70            distance = math.sqrt(distance_x**2 + distance_y**2)
 71
 72            # Normalise the distance
 73            distance /= normalise_factor
 74
 75            # Add the distance to the total distance
 76            total_distance += distance
 77
 78        # Calculate the angle of landmarks.left, landmarks.apex, landmarks.right
 79        # This is the angle of the triangle
 80        A, B, C = (
 81            np.array(landmarks.left),
 82            np.array(landmarks.apex),
 83            np.array(landmarks.right),
 84        )
 85        AB = np.linalg.norm(A - B)
 86        BC = np.linalg.norm(B - C)
 87        AC = np.linalg.norm(A - C)
 88        # in radians
 89        angle = np.arccos((BC**2 + AB**2 - AC**2) / (2 * BC * AB))
 90
 91        curvature = round(angle / total_distance, 2)
 92
 93    else:
 94        raise ValueError(f"Curvature method {config.hip.curvature} not found")
 95
 96    return curvature
 97
 98
 99def draw_curvature(
100    hip: HipDataUS, overlay: Overlay, config: Config
101) -> Overlay:
102    """
103    Draw the curvature of the hip.
104
105    :param hip: HipDataUS: The HipDataUS object.
106    :param overlay: Overlay: The Overlay object.
107    :param config: Config: The Config object.
108
109    :return: Overlay: The Overlay object.
110    """
111    curvature = hip.get_metric(MetricUS.CURVATURE)
112
113    if config.visuals.display_full_metric_names:
114        title = "Curvature"
115    else:
116        title = "Cur"
117
118    if curvature != 0:
119        overlay.draw_text(
120            f"{title}: {curvature}",
121            int(hip.landmarks.left[0]),
122            int(hip.landmarks.left[1] - 80),
123            header="h2",
124        )
125
126    return overlay
def find_curvature( landmarks: retuve.hip_us.classes.general.LandmarksUS, shape: Tuple, config: retuve.keyphrases.config.Config) -> float:
36def find_curvature(
37    landmarks: LandmarksUS, shape: Tuple, config: Config
38) -> float:
39    """
40    Calculate the curvature of the hip.
41
42    :param landmarks: LandmarksUS: The landmarks object.
43    :param shape: Tuple: The shape of the image.
44    :param config: Config: The Config object.
45
46    :return: float: The curvature of the hip.
47    """
48    if not (
49        landmarks and landmarks.left and landmarks.apex and landmarks.right
50    ):
51        return 0
52
53    if config.hip.curvature_method == Curvature.RADIST:
54        width = shape[1]
55
56        # This normalisation factor can handle bad crmodes,
57        # As well as off-center scans
58        normalise_factor = abs((width // 2) - landmarks.left[0])
59
60        total_distance = 0
61
62        for a, b in [
63            (landmarks.left, landmarks.apex),
64            (landmarks.right, landmarks.apex),
65        ]:
66            # Get the distance between two points in 2D space
67            distance_x = abs(a[0] - b[0])
68            distance_y = abs(a[1] - b[1])
69
70            # Calculate the distance between the two points
71            distance = math.sqrt(distance_x**2 + distance_y**2)
72
73            # Normalise the distance
74            distance /= normalise_factor
75
76            # Add the distance to the total distance
77            total_distance += distance
78
79        # Calculate the angle of landmarks.left, landmarks.apex, landmarks.right
80        # This is the angle of the triangle
81        A, B, C = (
82            np.array(landmarks.left),
83            np.array(landmarks.apex),
84            np.array(landmarks.right),
85        )
86        AB = np.linalg.norm(A - B)
87        BC = np.linalg.norm(B - C)
88        AC = np.linalg.norm(A - C)
89        # in radians
90        angle = np.arccos((BC**2 + AB**2 - AC**2) / (2 * BC * AB))
91
92        curvature = round(angle / total_distance, 2)
93
94    else:
95        raise ValueError(f"Curvature method {config.hip.curvature} not found")
96
97    return curvature

Calculate the curvature of the hip.

Parameters
  • landmarks: LandmarksUS: The landmarks object.
  • shape: Tuple: The shape of the image.
  • config: Config: The Config object.
Returns

float: The curvature of the hip.

100def draw_curvature(
101    hip: HipDataUS, overlay: Overlay, config: Config
102) -> Overlay:
103    """
104    Draw the curvature of the hip.
105
106    :param hip: HipDataUS: The HipDataUS object.
107    :param overlay: Overlay: The Overlay object.
108    :param config: Config: The Config object.
109
110    :return: Overlay: The Overlay object.
111    """
112    curvature = hip.get_metric(MetricUS.CURVATURE)
113
114    if config.visuals.display_full_metric_names:
115        title = "Curvature"
116    else:
117        title = "Cur"
118
119    if curvature != 0:
120        overlay.draw_text(
121            f"{title}: {curvature}",
122            int(hip.landmarks.left[0]),
123            int(hip.landmarks.left[1] - 80),
124            header="h2",
125        )
126
127    return overlay

Draw the curvature of the hip.

Parameters
  • hip: HipDataUS: The HipDataUS object.
  • overlay: Overlay: The Overlay object.
  • config: Config: The Config object.
Returns

Overlay: The Overlay object.