retuve.hip_us.handlers.side
Handles 3D and 2D Sweep Hip Ultrasounds that do not have the Anterior and Posterior sides correctly set.
Retuve is set up to have the Anterior side first and the Posterior side second.
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 3D and 2D Sweep Hip Ultrasounds that do not have the 17Anterior and Posterior sides correctly set. 18 19Retuve is set up to have the Anterior side first and the Posterior side second. 20""" 21 22from typing import List, Tuple 23 24from retuve.classes.seg import SegFrameObjects 25from retuve.hip_us.classes.enums import HipLabelsUS, Side 26from retuve.hip_us.classes.general import HipDatasUS, HipDataUS 27 28 29def get_side_metainfo( 30 hip: HipDataUS, results: List[SegFrameObjects] 31) -> Tuple[Tuple[int], List[int]]: 32 """ 33 Get the side metainfo for a HipDataUS object. 34 35 :param hip: HipDataUS object. 36 :param results: List of SegFrameObjects. 37 """ 38 if not hip.landmarks: 39 return None, None 40 41 left = hip.landmarks.left 42 apex = hip.landmarks.apex 43 44 mid = (left[0] + apex[0]) / 2, (left[1] + apex[1]) / 2 45 46 illium = [ 47 seg_obj 48 for seg_obj in results 49 if seg_obj.cls == HipLabelsUS.IlliumAndAcetabulum 50 ] 51 52 if len(illium) == 0: 53 return None, None 54 55 illium = illium[0] 56 57 illium_midline = illium.midline 58 59 # reverse x and y in midline 60 illium_midline = [(y, x) for x, y in illium_midline] 61 62 closest_illium = min( 63 illium_midline, 64 key=lambda point: abs(point[0] - mid[0]), 65 ) 66 67 return closest_illium, mid 68 69 70def set_side( 71 hip_datas: HipDatasUS, 72 results: List[SegFrameObjects], 73 allow_flipping, 74) -> Tuple[HipDatasUS, List[SegFrameObjects]]: 75 """ 76 Set the side for each HipDataUS object in the HipDatasUS object. 77 78 Returns the HipDatasUS object and the results with the Anterior side first. 79 80 :param hip_datas: HipDatasUS object. 81 :param results: List of SegFrameObjects. 82 :param allow_flipping: Boolean indicating if flipping is allowed. 83 84 :return: Tuple of HipDatasUS object and List of SegFrameObjects. 85 """ 86 87 # split the hip datas into two sides, based on graf frame 88 front_side = [] 89 back_side = [] 90 91 for hip_data, seg_frame_objs in zip(hip_datas, results): 92 if hip_data.landmarks is None or hip_data.landmarks.apex is None: 93 continue 94 95 closest_illium, mid = get_side_metainfo(hip_data, seg_frame_objs) 96 97 if closest_illium is None: 98 continue 99 100 # Find the y distance between the midline points 101 delta = mid[1] - closest_illium[1] 102 103 if hip_data.frame_no < hip_datas.graf_frame: 104 front_side.append(delta) 105 else: 106 back_side.append(delta) 107 108 for side in [("Front", front_side), ("Back", back_side)]: # noqa 109 if len(side[1]) == 0: 110 hip_datas.recorded_error.append(f"No {side[0]} Side.") 111 hip_datas.recorded_error.critical = True 112 113 if len(front_side) == 0: 114 front_side = [0] 115 if len(back_side) == 0: 116 back_side = [0] 117 118 if front_side == [0] and back_side == [0]: 119 hip_datas.recorded_error.append("No Side Detected.") 120 return hip_datas, results 121 122 back_side_delta = sum(back_side) / len(back_side) 123 front_side_delta = sum(front_side) / len(front_side) 124 125 flip_frames = False 126 if (back_side_delta > front_side_delta) and allow_flipping: 127 hip_datas.recorded_error.append("Swapped Post and Ant") 128 hip_datas.hip_datas = hip_datas.hip_datas[::-1] 129 results = results[::-1] 130 hip_datas.graf_frame = len(hip_datas) - hip_datas.graf_frame 131 flip_frames = True 132 133 # set the side for each hip data 134 for i, hip_data in enumerate(hip_datas): 135 if flip_frames: 136 hip_data.frame_no = i 137 if hip_data.frame_no < hip_datas.graf_frame: 138 hip_data.side = Side.ANT 139 else: 140 hip_data.side = Side.POST 141 142 return hip_datas, results
def
get_side_metainfo( hip: retuve.hip_us.classes.general.HipDataUS, results: List[retuve.classes.seg.SegFrameObjects]) -> Tuple[Tuple[int], List[int]]:
30def get_side_metainfo( 31 hip: HipDataUS, results: List[SegFrameObjects] 32) -> Tuple[Tuple[int], List[int]]: 33 """ 34 Get the side metainfo for a HipDataUS object. 35 36 :param hip: HipDataUS object. 37 :param results: List of SegFrameObjects. 38 """ 39 if not hip.landmarks: 40 return None, None 41 42 left = hip.landmarks.left 43 apex = hip.landmarks.apex 44 45 mid = (left[0] + apex[0]) / 2, (left[1] + apex[1]) / 2 46 47 illium = [ 48 seg_obj 49 for seg_obj in results 50 if seg_obj.cls == HipLabelsUS.IlliumAndAcetabulum 51 ] 52 53 if len(illium) == 0: 54 return None, None 55 56 illium = illium[0] 57 58 illium_midline = illium.midline 59 60 # reverse x and y in midline 61 illium_midline = [(y, x) for x, y in illium_midline] 62 63 closest_illium = min( 64 illium_midline, 65 key=lambda point: abs(point[0] - mid[0]), 66 ) 67 68 return closest_illium, mid
Get the side metainfo for a HipDataUS object.
Parameters
- hip: HipDataUS object.
- results: List of SegFrameObjects.
def
set_side( hip_datas: retuve.hip_us.classes.general.HipDatasUS, results: List[retuve.classes.seg.SegFrameObjects], allow_flipping) -> Tuple[retuve.hip_us.classes.general.HipDatasUS, List[retuve.classes.seg.SegFrameObjects]]:
71def set_side( 72 hip_datas: HipDatasUS, 73 results: List[SegFrameObjects], 74 allow_flipping, 75) -> Tuple[HipDatasUS, List[SegFrameObjects]]: 76 """ 77 Set the side for each HipDataUS object in the HipDatasUS object. 78 79 Returns the HipDatasUS object and the results with the Anterior side first. 80 81 :param hip_datas: HipDatasUS object. 82 :param results: List of SegFrameObjects. 83 :param allow_flipping: Boolean indicating if flipping is allowed. 84 85 :return: Tuple of HipDatasUS object and List of SegFrameObjects. 86 """ 87 88 # split the hip datas into two sides, based on graf frame 89 front_side = [] 90 back_side = [] 91 92 for hip_data, seg_frame_objs in zip(hip_datas, results): 93 if hip_data.landmarks is None or hip_data.landmarks.apex is None: 94 continue 95 96 closest_illium, mid = get_side_metainfo(hip_data, seg_frame_objs) 97 98 if closest_illium is None: 99 continue 100 101 # Find the y distance between the midline points 102 delta = mid[1] - closest_illium[1] 103 104 if hip_data.frame_no < hip_datas.graf_frame: 105 front_side.append(delta) 106 else: 107 back_side.append(delta) 108 109 for side in [("Front", front_side), ("Back", back_side)]: # noqa 110 if len(side[1]) == 0: 111 hip_datas.recorded_error.append(f"No {side[0]} Side.") 112 hip_datas.recorded_error.critical = True 113 114 if len(front_side) == 0: 115 front_side = [0] 116 if len(back_side) == 0: 117 back_side = [0] 118 119 if front_side == [0] and back_side == [0]: 120 hip_datas.recorded_error.append("No Side Detected.") 121 return hip_datas, results 122 123 back_side_delta = sum(back_side) / len(back_side) 124 front_side_delta = sum(front_side) / len(front_side) 125 126 flip_frames = False 127 if (back_side_delta > front_side_delta) and allow_flipping: 128 hip_datas.recorded_error.append("Swapped Post and Ant") 129 hip_datas.hip_datas = hip_datas.hip_datas[::-1] 130 results = results[::-1] 131 hip_datas.graf_frame = len(hip_datas) - hip_datas.graf_frame 132 flip_frames = True 133 134 # set the side for each hip data 135 for i, hip_data in enumerate(hip_datas): 136 if flip_frames: 137 hip_data.frame_no = i 138 if hip_data.frame_no < hip_datas.graf_frame: 139 hip_data.side = Side.ANT 140 else: 141 hip_data.side = Side.POST 142 143 return hip_datas, results
Set the side for each HipDataUS object in the HipDatasUS object.
Returns the HipDatasUS object and the results with the Anterior side first.
Parameters
- hip_datas: HipDatasUS object.
- results: List of SegFrameObjects.
- allow_flipping: Boolean indicating if flipping is allowed.
Returns
Tuple of HipDatasUS object and List of SegFrameObjects.