retuve.hip_us.multiframe.main
High Level Functions for Multiframe Analysis
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""" 16High Level Functions for Multiframe Analysis 17""" 18 19import time 20from typing import List 21 22import numpy as np 23import open3d as o3d 24from numpy import ndarray as NDArray 25from plotly.graph_objects import Figure 26 27from retuve.classes.seg import SegFrameObjects 28from retuve.hip_us.classes.enums import Side 29from retuve.hip_us.classes.general import HipDatasUS, Metric3D 30from retuve.hip_us.metrics.aca import get_aca 31from retuve.hip_us.metrics.c_ratio import get_centering_ratio 32from retuve.hip_us.multiframe.models import ( 33 build_3d_visual, 34 get_femoral_sphere, 35 get_illium_mesh, 36) 37from retuve.hip_us.typing import CoordinatesArray3D, FemoralHeadSphere 38from retuve.keyphrases.config import Config 39from retuve.keyphrases.enums import MetricUS 40from retuve.logs import ulogger 41from retuve.utils import rmean 42 43 44class FemSphere: 45 def __init__(self, center: CoordinatesArray3D, radius: float): 46 self.center = center 47 self.radius = radius 48 49 50def get_3d_metrics_and_visuals( 51 hip_datas: HipDatasUS, results: List[SegFrameObjects], config: Config 52) -> tuple[ 53 HipDatasUS, 54 Figure, 55 FemSphere, 56 o3d.geometry.TriangleMesh, 57 NDArray[np.float64], 58 FemoralHeadSphere, 59 CoordinatesArray3D, 60 CoordinatesArray3D, 61]: 62 """ 63 Get the 3D metrics and visuals for the hip US module. 64 65 :param hip_datas: The hip data. 66 :param results: The segmentation results. 67 :param config: The configuration object. 68 69 :return: The hip data, the 3D visual, the femoral sphere, 70 the illium mesh, the apex points, the femoral sphere, 71 the average normals data of the ACA Vectors, and the 72 normals data of the ACA Vectors. 73 """ 74 start = time.time() 75 illium_mesh, apex_points = get_illium_mesh(hip_datas, results, config) 76 visual_3d = None 77 78 if illium_mesh is None: 79 hip_datas.recorded_error.append("No Illium Mesh.") 80 hip_datas.recorded_error.critical = True 81 82 femoral_sphere, radius = get_femoral_sphere(hip_datas, config) 83 84 if illium_mesh: 85 aca, avg_normals_data, normals_data, aca_error_msg = get_aca( 86 illium_mesh, 87 apex_points, 88 hip_datas.grafs_hip, 89 len(hip_datas), 90 config, 91 ) 92 93 hip_datas.recorded_error.append(aca_error_msg) 94 else: 95 aca = None 96 avg_normals_data = None 97 normals_data = None 98 99 if femoral_sphere and illium_mesh: 100 c_ratio, cr_points = get_centering_ratio( 101 illium_mesh, femoral_sphere, hip_datas, config 102 ) 103 hip_datas.cr_points = cr_points 104 105 else: 106 c_ratio = 0 107 cr_points = [] 108 109 if illium_mesh and femoral_sphere: 110 visual_3d = build_3d_visual( 111 illium_mesh, 112 femoral_sphere, 113 avg_normals_data, 114 normals_data, 115 cr_points, 116 hip_datas, 117 config, 118 ) 119 120 if aca and MetricUS.ACA in config.hip.measurements: 121 aca_metric = Metric3D( 122 name=MetricUS.ACA, 123 graf=aca[Side.GRAF], 124 post=aca[Side.POST], 125 ant=aca[Side.ANT], 126 ) 127 hip_datas.metrics.append(aca_metric) 128 129 if c_ratio and MetricUS.CENTERING_RATIO in config.hip.measurements: 130 c_ratio = Metric3D( 131 name=MetricUS.CENTERING_RATIO, 132 full=c_ratio, 133 ) 134 hip_datas.metrics.append(c_ratio) 135 136 for name in config.hip.measurements: 137 if not any(metric.name == name for metric in hip_datas.metrics): 138 post_values = [ 139 hip_data.get_metric(name) 140 for hip_data in hip_datas 141 if hip_data.side == Side.POST 142 and hip_data.get_metric(name) != 0 143 ] or [0] 144 ant_values = [ 145 hip_data.get_metric(name) 146 for hip_data in hip_datas 147 if hip_data.side == Side.ANT and hip_data.get_metric(name) != 0 148 ] or [0] 149 150 graf_value = 0 151 if hip_datas.graf_frame: 152 graf_value = hip_datas.grafs_hip.get_metric(name) 153 154 hip_datas.metrics.append( 155 Metric3D( 156 name=name, 157 graf=graf_value, 158 post=rmean(post_values), 159 ant=rmean(ant_values), 160 ) 161 ) 162 163 if all(metric.post == 0 for metric in hip_datas.metrics): 164 hip_datas.recorded_error.append("No Posterior values recorded.") 165 hip_datas.recorded_error.critical = True 166 167 if all(metric.ant == 0 for metric in hip_datas.metrics): 168 hip_datas.recorded_error.append("No Anterior values recorded.") 169 hip_datas.recorded_error.critical = True 170 if len(cr_points) != 0: 171 fem_sph = FemSphere(cr_points[0], radius) 172 else: 173 fem_sph = None 174 175 ulogger.info( 176 f"Time for all 3D Elements: {round(time.time() - start, 2)} s" 177 ) 178 179 return ( 180 hip_datas, 181 visual_3d, 182 fem_sph, 183 illium_mesh, 184 apex_points, 185 femoral_sphere, 186 avg_normals_data, 187 normals_data, 188 )
class
FemSphere:
45class FemSphere: 46 def __init__(self, center: CoordinatesArray3D, radius: float): 47 self.center = center 48 self.radius = radius
def
get_3d_metrics_and_visuals( hip_datas: retuve.hip_us.classes.general.HipDatasUS, results: List[retuve.classes.seg.SegFrameObjects], config: retuve.keyphrases.config.Config) -> tuple[retuve.hip_us.classes.general.HipDatasUS, plotly.graph_objs._figure.Figure, FemSphere, open3d.cpu.pybind.geometry.TriangleMesh, numpy.ndarray[numpy.float64], typing.Tuple[numpy.ndarray, numpy.ndarray, numpy.ndarray], numpy.ndarray[typing.Any, numpy.dtype[numpy.float64]], numpy.ndarray[typing.Any, numpy.dtype[numpy.float64]]]:
51def get_3d_metrics_and_visuals( 52 hip_datas: HipDatasUS, results: List[SegFrameObjects], config: Config 53) -> tuple[ 54 HipDatasUS, 55 Figure, 56 FemSphere, 57 o3d.geometry.TriangleMesh, 58 NDArray[np.float64], 59 FemoralHeadSphere, 60 CoordinatesArray3D, 61 CoordinatesArray3D, 62]: 63 """ 64 Get the 3D metrics and visuals for the hip US module. 65 66 :param hip_datas: The hip data. 67 :param results: The segmentation results. 68 :param config: The configuration object. 69 70 :return: The hip data, the 3D visual, the femoral sphere, 71 the illium mesh, the apex points, the femoral sphere, 72 the average normals data of the ACA Vectors, and the 73 normals data of the ACA Vectors. 74 """ 75 start = time.time() 76 illium_mesh, apex_points = get_illium_mesh(hip_datas, results, config) 77 visual_3d = None 78 79 if illium_mesh is None: 80 hip_datas.recorded_error.append("No Illium Mesh.") 81 hip_datas.recorded_error.critical = True 82 83 femoral_sphere, radius = get_femoral_sphere(hip_datas, config) 84 85 if illium_mesh: 86 aca, avg_normals_data, normals_data, aca_error_msg = get_aca( 87 illium_mesh, 88 apex_points, 89 hip_datas.grafs_hip, 90 len(hip_datas), 91 config, 92 ) 93 94 hip_datas.recorded_error.append(aca_error_msg) 95 else: 96 aca = None 97 avg_normals_data = None 98 normals_data = None 99 100 if femoral_sphere and illium_mesh: 101 c_ratio, cr_points = get_centering_ratio( 102 illium_mesh, femoral_sphere, hip_datas, config 103 ) 104 hip_datas.cr_points = cr_points 105 106 else: 107 c_ratio = 0 108 cr_points = [] 109 110 if illium_mesh and femoral_sphere: 111 visual_3d = build_3d_visual( 112 illium_mesh, 113 femoral_sphere, 114 avg_normals_data, 115 normals_data, 116 cr_points, 117 hip_datas, 118 config, 119 ) 120 121 if aca and MetricUS.ACA in config.hip.measurements: 122 aca_metric = Metric3D( 123 name=MetricUS.ACA, 124 graf=aca[Side.GRAF], 125 post=aca[Side.POST], 126 ant=aca[Side.ANT], 127 ) 128 hip_datas.metrics.append(aca_metric) 129 130 if c_ratio and MetricUS.CENTERING_RATIO in config.hip.measurements: 131 c_ratio = Metric3D( 132 name=MetricUS.CENTERING_RATIO, 133 full=c_ratio, 134 ) 135 hip_datas.metrics.append(c_ratio) 136 137 for name in config.hip.measurements: 138 if not any(metric.name == name for metric in hip_datas.metrics): 139 post_values = [ 140 hip_data.get_metric(name) 141 for hip_data in hip_datas 142 if hip_data.side == Side.POST 143 and hip_data.get_metric(name) != 0 144 ] or [0] 145 ant_values = [ 146 hip_data.get_metric(name) 147 for hip_data in hip_datas 148 if hip_data.side == Side.ANT and hip_data.get_metric(name) != 0 149 ] or [0] 150 151 graf_value = 0 152 if hip_datas.graf_frame: 153 graf_value = hip_datas.grafs_hip.get_metric(name) 154 155 hip_datas.metrics.append( 156 Metric3D( 157 name=name, 158 graf=graf_value, 159 post=rmean(post_values), 160 ant=rmean(ant_values), 161 ) 162 ) 163 164 if all(metric.post == 0 for metric in hip_datas.metrics): 165 hip_datas.recorded_error.append("No Posterior values recorded.") 166 hip_datas.recorded_error.critical = True 167 168 if all(metric.ant == 0 for metric in hip_datas.metrics): 169 hip_datas.recorded_error.append("No Anterior values recorded.") 170 hip_datas.recorded_error.critical = True 171 if len(cr_points) != 0: 172 fem_sph = FemSphere(cr_points[0], radius) 173 else: 174 fem_sph = None 175 176 ulogger.info( 177 f"Time for all 3D Elements: {round(time.time() - start, 2)} s" 178 ) 179 180 return ( 181 hip_datas, 182 visual_3d, 183 fem_sph, 184 illium_mesh, 185 apex_points, 186 femoral_sphere, 187 avg_normals_data, 188 normals_data, 189 )
Get the 3D metrics and visuals for the hip US module.
Parameters
- hip_datas: The hip data.
- results: The segmentation results.
- config: The configuration object.
Returns
The hip data, the 3D visual, the femoral sphere, the illium mesh, the apex points, the femoral sphere, the average normals data of the ACA Vectors, and the normals data of the ACA Vectors.