retuve.hip_us.handlers.bad_data
Handles bad Hip Data Objects by removing outliers and empty frames.
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""" 16Handles bad Hip Data Objects by removing outliers and empty frames. 17""" 18 19from typing import List 20 21import numpy as np 22 23from retuve.hip_us.classes.general import HipDatasUS, HipDataUS 24from retuve.hip_us.metrics.alpha import bad_alpha 25from retuve.hip_us.metrics.coverage import bad_coverage 26from retuve.keyphrases.config import Config 27from retuve.keyphrases.enums import HipMode 28 29 30def remove_outliers(hip_datas: HipDatasUS, config: Config) -> List[bool]: 31 """ 32 Remove outliers from the HipDatasUS object. 33 34 :param hip_datas: HipDatasUS object. 35 :param config: Config object. 36 37 :return: List of booleans indicating which frames to keep. 38 """ 39 pred_made = [(True if hip.marked() else False) for hip in hip_datas] 40 41 # Use this as a sliding window to find the position where 42 # the most Trues fit in the window 43 # Get total number of True values 44 total_true = sum(pred_made) 45 46 # Sliding window to find the position where the most Trues fit in the window 47 max_true = 0 48 max_true_index = 0 49 50 for i in range(len(pred_made) - total_true + 1): 51 current_window_true = sum(pred_made[i : i + total_true]) 52 if current_window_true > max_true: 53 max_true = current_window_true 54 max_true_index = i 55 56 # Create a list of Trues and Falses 57 keep = [False] * len(pred_made) 58 for i in range(max_true_index, max_true_index + total_true): 59 if pred_made[i]: 60 keep[i] = True 61 62 return keep 63 64 65def left_apex_line_flat(hip: HipDataUS) -> bool: 66 """ 67 Check if the left apex line is flat. 68 69 :param hip: HipDataUS object. 70 71 :return: Boolean indicating if the left apex line is flat. 72 """ 73 if hip.landmarks.left is None or hip.landmarks.apex is None: 74 return True 75 76 C, A, B = ( 77 np.array(hip.landmarks.left), 78 np.array(hip.landmarks.apex), 79 np.array((hip.landmarks.apex[0], hip.landmarks.left[1])), 80 ) 81 82 a = np.linalg.norm(C - B) 83 b = np.linalg.norm(C - A) 84 85 angle = np.arccos((a**2 + b**2 - np.linalg.norm(A - B) ** 2) / (2 * a * b)) 86 angle = np.degrees(angle) 87 88 return abs(angle) < 10 89 90 91def apex_right_points_too_close(hip: HipDataUS) -> bool: 92 """ 93 Check if the apex and right points are too close. 94 95 :param hip: HipDataUS object. 96 97 :return: Boolean indicating if the apex and right points are too close. 98 """ 99 if hip.landmarks.right is None or hip.landmarks.apex is None: 100 return True 101 102 return ( 103 np.linalg.norm( 104 np.array(hip.landmarks.right) - np.array(hip.landmarks.apex) 105 ) 106 < 30 107 ) 108 109 110def handle_bad_frames(hip_datas: HipDatasUS, config: Config) -> HipDatasUS: 111 """ 112 Handle bad frames by removing outliers and empty frames. 113 114 :param hip_datas: HipDatasUS object. 115 :param config: Config object. 116 117 :return: HipDatasUS object. 118 """ 119 120 if config.batch.hip_mode == HipMode.US2DSW: 121 keep = [(True if hip.marked() else False) for hip in hip_datas] 122 else: 123 keep = remove_outliers(hip_datas, config) 124 125 bad_frame_reasons = {} 126 127 for i, (hip, rejection_reasons) in enumerate( 128 zip(hip_datas, hip_datas.all_seg_rejection_reasons) 129 ): 130 131 empty_hip = HipDataUS( 132 frame_no=hip.frame_no, 133 ) 134 135 if not keep[i]: 136 hip_datas[i] = empty_hip 137 if config.batch.hip_mode == HipMode.US2DSW: 138 bad_frame_reasons[i] = " ".join(rejection_reasons) 139 if not rejection_reasons: 140 bad_frame_reasons[i] = "Not Enough Data" 141 continue 142 143 if (not hip.metrics) or all( 144 metric.value == 0 for metric in hip.metrics 145 ): 146 hip_datas[i] = empty_hip 147 bad_frame_reasons[i] = "No Metrics" 148 continue 149 150 if hip.landmarks is None: 151 hip_datas[i] = empty_hip 152 bad_frame_reasons[i] = "No Landmarks" 153 continue 154 155 if bad_alpha(hip): 156 hip_datas[i] = empty_hip 157 bad_frame_reasons[i] = "Alpha Angle Non-Sensical" 158 continue 159 160 if not left_apex_line_flat(hip): 161 hip_datas[i] = empty_hip 162 bad_frame_reasons[i] = "Ilium Line not Flat" 163 continue 164 165 if bad_coverage(hip): 166 hip_datas[i] = empty_hip 167 bad_frame_reasons[i] = "Coverage Value Non-Sensical" 168 continue 169 170 if apex_right_points_too_close(hip): 171 hip_datas[i] = empty_hip 172 bad_frame_reasons[i] = "Apex and Right Too Close" 173 continue 174 175 hip_datas.bad_frame_reasons = bad_frame_reasons 176 return hip_datas
def
remove_outliers( hip_datas: retuve.hip_us.classes.general.HipDatasUS, config: retuve.keyphrases.config.Config) -> List[bool]:
31def remove_outliers(hip_datas: HipDatasUS, config: Config) -> List[bool]: 32 """ 33 Remove outliers from the HipDatasUS object. 34 35 :param hip_datas: HipDatasUS object. 36 :param config: Config object. 37 38 :return: List of booleans indicating which frames to keep. 39 """ 40 pred_made = [(True if hip.marked() else False) for hip in hip_datas] 41 42 # Use this as a sliding window to find the position where 43 # the most Trues fit in the window 44 # Get total number of True values 45 total_true = sum(pred_made) 46 47 # Sliding window to find the position where the most Trues fit in the window 48 max_true = 0 49 max_true_index = 0 50 51 for i in range(len(pred_made) - total_true + 1): 52 current_window_true = sum(pred_made[i : i + total_true]) 53 if current_window_true > max_true: 54 max_true = current_window_true 55 max_true_index = i 56 57 # Create a list of Trues and Falses 58 keep = [False] * len(pred_made) 59 for i in range(max_true_index, max_true_index + total_true): 60 if pred_made[i]: 61 keep[i] = True 62 63 return keep
Remove outliers from the HipDatasUS object.
Parameters
- hip_datas: HipDatasUS object.
- config: Config object.
Returns
List of booleans indicating which frames to keep.
66def left_apex_line_flat(hip: HipDataUS) -> bool: 67 """ 68 Check if the left apex line is flat. 69 70 :param hip: HipDataUS object. 71 72 :return: Boolean indicating if the left apex line is flat. 73 """ 74 if hip.landmarks.left is None or hip.landmarks.apex is None: 75 return True 76 77 C, A, B = ( 78 np.array(hip.landmarks.left), 79 np.array(hip.landmarks.apex), 80 np.array((hip.landmarks.apex[0], hip.landmarks.left[1])), 81 ) 82 83 a = np.linalg.norm(C - B) 84 b = np.linalg.norm(C - A) 85 86 angle = np.arccos((a**2 + b**2 - np.linalg.norm(A - B) ** 2) / (2 * a * b)) 87 angle = np.degrees(angle) 88 89 return abs(angle) < 10
Check if the left apex line is flat.
Parameters
- hip: HipDataUS object.
Returns
Boolean indicating if the left apex line is flat.
92def apex_right_points_too_close(hip: HipDataUS) -> bool: 93 """ 94 Check if the apex and right points are too close. 95 96 :param hip: HipDataUS object. 97 98 :return: Boolean indicating if the apex and right points are too close. 99 """ 100 if hip.landmarks.right is None or hip.landmarks.apex is None: 101 return True 102 103 return ( 104 np.linalg.norm( 105 np.array(hip.landmarks.right) - np.array(hip.landmarks.apex) 106 ) 107 < 30 108 )
Check if the apex and right points are too close.
Parameters
- hip: HipDataUS object.
Returns
Boolean indicating if the apex and right points are too close.
def
handle_bad_frames( hip_datas: retuve.hip_us.classes.general.HipDatasUS, config: retuve.keyphrases.config.Config) -> retuve.hip_us.classes.general.HipDatasUS:
111def handle_bad_frames(hip_datas: HipDatasUS, config: Config) -> HipDatasUS: 112 """ 113 Handle bad frames by removing outliers and empty frames. 114 115 :param hip_datas: HipDatasUS object. 116 :param config: Config object. 117 118 :return: HipDatasUS object. 119 """ 120 121 if config.batch.hip_mode == HipMode.US2DSW: 122 keep = [(True if hip.marked() else False) for hip in hip_datas] 123 else: 124 keep = remove_outliers(hip_datas, config) 125 126 bad_frame_reasons = {} 127 128 for i, (hip, rejection_reasons) in enumerate( 129 zip(hip_datas, hip_datas.all_seg_rejection_reasons) 130 ): 131 132 empty_hip = HipDataUS( 133 frame_no=hip.frame_no, 134 ) 135 136 if not keep[i]: 137 hip_datas[i] = empty_hip 138 if config.batch.hip_mode == HipMode.US2DSW: 139 bad_frame_reasons[i] = " ".join(rejection_reasons) 140 if not rejection_reasons: 141 bad_frame_reasons[i] = "Not Enough Data" 142 continue 143 144 if (not hip.metrics) or all( 145 metric.value == 0 for metric in hip.metrics 146 ): 147 hip_datas[i] = empty_hip 148 bad_frame_reasons[i] = "No Metrics" 149 continue 150 151 if hip.landmarks is None: 152 hip_datas[i] = empty_hip 153 bad_frame_reasons[i] = "No Landmarks" 154 continue 155 156 if bad_alpha(hip): 157 hip_datas[i] = empty_hip 158 bad_frame_reasons[i] = "Alpha Angle Non-Sensical" 159 continue 160 161 if not left_apex_line_flat(hip): 162 hip_datas[i] = empty_hip 163 bad_frame_reasons[i] = "Ilium Line not Flat" 164 continue 165 166 if bad_coverage(hip): 167 hip_datas[i] = empty_hip 168 bad_frame_reasons[i] = "Coverage Value Non-Sensical" 169 continue 170 171 if apex_right_points_too_close(hip): 172 hip_datas[i] = empty_hip 173 bad_frame_reasons[i] = "Apex and Right Too Close" 174 continue 175 176 hip_datas.bad_frame_reasons = bad_frame_reasons 177 return hip_datas
Handle bad frames by removing outliers and empty frames.
Parameters
- hip_datas: HipDatasUS object.
- config: Config object.
Returns
HipDatasUS object.