Update app.py
Browse files
app.py
CHANGED
|
@@ -115,7 +115,6 @@ class DicomAnalyzer:
|
|
| 115 |
except Exception as e:
|
| 116 |
print(f"Error normalizing image: {str(e)}")
|
| 117 |
return None
|
| 118 |
-
|
| 119 |
def reset_view(self):
|
| 120 |
self.zoom_factor = 1.0
|
| 121 |
self.pan_x = 0
|
|
@@ -230,11 +229,8 @@ class DicomAnalyzer:
|
|
| 230 |
new_height = int(height * self.zoom_factor)
|
| 231 |
new_width = int(width * self.zoom_factor)
|
| 232 |
|
| 233 |
-
zoomed = cv2.resize(
|
| 234 |
-
|
| 235 |
-
(new_width, new_height),
|
| 236 |
-
interpolation=cv2.INTER_CUBIC
|
| 237 |
-
)
|
| 238 |
|
| 239 |
zoomed_bgr = cv2.cvtColor(zoomed, cv2.COLOR_RGB2BGR)
|
| 240 |
|
|
@@ -243,28 +239,24 @@ class DicomAnalyzer:
|
|
| 243 |
zoomed_y = int(y * self.zoom_factor)
|
| 244 |
zoomed_radius = int((diameter/2.0) * self.zoom_factor)
|
| 245 |
|
| 246 |
-
cv2.circle(
|
| 247 |
-
|
| 248 |
-
|
| 249 |
-
|
| 250 |
-
|
| 251 |
-
|
| 252 |
-
lineType=cv2.LINE_AA
|
| 253 |
-
)
|
| 254 |
|
| 255 |
num_points = 8
|
| 256 |
for i in range(num_points):
|
| 257 |
angle = 2 * np.pi * i / num_points
|
| 258 |
point_x = int(zoomed_x + zoomed_radius * np.cos(angle))
|
| 259 |
point_y = int(zoomed_y + zoomed_radius * np.sin(angle))
|
| 260 |
-
cv2.circle(
|
| 261 |
-
|
| 262 |
-
|
| 263 |
-
|
| 264 |
-
|
| 265 |
-
|
| 266 |
-
lineType=cv2.LINE_AA
|
| 267 |
-
)
|
| 268 |
|
| 269 |
zoomed = cv2.cvtColor(zoomed_bgr, cv2.COLOR_BGR2RGB)
|
| 270 |
|
|
@@ -318,7 +310,6 @@ class DicomAnalyzer:
|
|
| 318 |
logger.error(error_msg)
|
| 319 |
logger.error(traceback.format_exc())
|
| 320 |
return None, error_msg
|
| 321 |
-
|
| 322 |
def add_formulas_to_template(self, ws, row_pair, col_group, red_font):
|
| 323 |
try:
|
| 324 |
base_col = col_group[1] # Mean column
|
|
@@ -335,10 +326,7 @@ class DicomAnalyzer:
|
|
| 335 |
cell1.alignment = openpyxl.styles.Alignment(horizontal='center')
|
| 336 |
|
| 337 |
# CNR Formula for second row
|
| 338 |
-
formula2 = (
|
| 339 |
-
f"=IF(OR({std_col}{row2}=0,{base_col}{row1}=0,{base_col}{row2}=0),"
|
| 340 |
-
f"0,({base_col}{row1}-{base_col}{row2})/{std_col}{row2})"
|
| 341 |
-
)
|
| 342 |
cell2 = ws[f"{formula_col}{row2}"]
|
| 343 |
cell2.value = formula2
|
| 344 |
cell2.font = red_font
|
|
@@ -383,10 +371,8 @@ class DicomAnalyzer:
|
|
| 383 |
(17, 18), (20, 21), (23, 24), (26, 27), (29, 30)
|
| 384 |
]
|
| 385 |
|
| 386 |
-
phantom_sizes = [
|
| 387 |
-
|
| 388 |
-
'(4.5mm)', '(4mm)', '(3.5mm)', '(3mm)', '(2.5mm)'
|
| 389 |
-
]
|
| 390 |
|
| 391 |
for i, size in enumerate(phantom_sizes):
|
| 392 |
header_cell = ws.cell(row=row_pairs[i][0]-1, column=1, value=size)
|
|
@@ -438,15 +424,15 @@ class DicomAnalyzer:
|
|
| 438 |
stddev_header.alignment = center_alignment
|
| 439 |
current_row += 1
|
| 440 |
|
| 441 |
-
# Calculate StdDev averages for each row pair
|
| 442 |
for i, size in enumerate(phantom_sizes):
|
| 443 |
-
row_number = row_pairs[i][0] # First row
|
| 444 |
stddev_values = []
|
| 445 |
|
| 446 |
for cols in column_groups:
|
| 447 |
stddev_col = cols[2] # StdDev column
|
| 448 |
cell_value = ws[f"{stddev_col}{row_number}"].value
|
| 449 |
-
if cell_value not in [0, None, '']: # Ignore zeros and empty
|
| 450 |
stddev_values.append(float(cell_value))
|
| 451 |
|
| 452 |
if stddev_values:
|
|
@@ -467,7 +453,7 @@ class DicomAnalyzer:
|
|
| 467 |
cnr_header.alignment = center_alignment
|
| 468 |
current_row += 1
|
| 469 |
|
| 470 |
-
# Calculate CNR averages for each row pair
|
| 471 |
for i, size in enumerate(phantom_sizes):
|
| 472 |
row_number = row_pairs[i][1] # Second row for CNR
|
| 473 |
cnr_references = []
|
|
@@ -515,6 +501,7 @@ class DicomAnalyzer:
|
|
| 515 |
logger.error(f"Error saving formatted results: {str(e)}")
|
| 516 |
return None, f"Error saving results: {str(e)}"
|
| 517 |
|
|
|
|
| 518 |
def _write_result_to_cells(self, ws, result, cols, row):
|
| 519 |
"""Helper method to write a single result to worksheet cells"""
|
| 520 |
center_alignment = openpyxl.styles.Alignment(horizontal='center')
|
|
@@ -570,7 +557,6 @@ class DicomAnalyzer:
|
|
| 570 |
self.marks.pop()
|
| 571 |
return self.update_display(), self.format_results()
|
| 572 |
|
| 573 |
-
# Interface Creation and Main Execution
|
| 574 |
def create_interface():
|
| 575 |
print("Creating interface...")
|
| 576 |
analyzer = DicomAnalyzer()
|
|
@@ -621,7 +607,7 @@ def create_interface():
|
|
| 621 |
- Use Reset All to clear all measurements
|
| 622 |
- Save Formatted Results will create Excel file with formulas
|
| 623 |
""")
|
| 624 |
-
|
| 625 |
def update_diameter(x):
|
| 626 |
analyzer.circle_diameter = float(x)
|
| 627 |
print(f"Diameter updated to: {x}")
|
|
@@ -630,7 +616,7 @@ def create_interface():
|
|
| 630 |
def save_formatted():
|
| 631 |
output_path = "analysis_results_formatted.xlsx"
|
| 632 |
return analyzer.save_formatted_results(output_path)
|
| 633 |
-
|
| 634 |
# Event handlers
|
| 635 |
file_input.change(
|
| 636 |
fn=analyzer.load_dicom,
|
|
@@ -745,4 +731,4 @@ if __name__ == "__main__":
|
|
| 745 |
print(f"Error launching application: {str(e)}")
|
| 746 |
logger.error(f"Error launching application: {str(e)}")
|
| 747 |
logger.error(traceback.format_exc())
|
| 748 |
-
raise e
|
|
|
|
| 115 |
except Exception as e:
|
| 116 |
print(f"Error normalizing image: {str(e)}")
|
| 117 |
return None
|
|
|
|
| 118 |
def reset_view(self):
|
| 119 |
self.zoom_factor = 1.0
|
| 120 |
self.pan_x = 0
|
|
|
|
| 229 |
new_height = int(height * self.zoom_factor)
|
| 230 |
new_width = int(width * self.zoom_factor)
|
| 231 |
|
| 232 |
+
zoomed = cv2.resize(self.original_display, (new_width, new_height),
|
| 233 |
+
interpolation=cv2.INTER_CUBIC)
|
|
|
|
|
|
|
|
|
|
| 234 |
|
| 235 |
zoomed_bgr = cv2.cvtColor(zoomed, cv2.COLOR_RGB2BGR)
|
| 236 |
|
|
|
|
| 239 |
zoomed_y = int(y * self.zoom_factor)
|
| 240 |
zoomed_radius = int((diameter/2.0) * self.zoom_factor)
|
| 241 |
|
| 242 |
+
cv2.circle(zoomed_bgr,
|
| 243 |
+
(zoomed_x, zoomed_y),
|
| 244 |
+
zoomed_radius,
|
| 245 |
+
self.CIRCLE_COLOR,
|
| 246 |
+
1,
|
| 247 |
+
lineType=cv2.LINE_AA)
|
|
|
|
|
|
|
| 248 |
|
| 249 |
num_points = 8
|
| 250 |
for i in range(num_points):
|
| 251 |
angle = 2 * np.pi * i / num_points
|
| 252 |
point_x = int(zoomed_x + zoomed_radius * np.cos(angle))
|
| 253 |
point_y = int(zoomed_y + zoomed_radius * np.sin(angle))
|
| 254 |
+
cv2.circle(zoomed_bgr,
|
| 255 |
+
(point_x, point_y),
|
| 256 |
+
1,
|
| 257 |
+
self.CIRCLE_COLOR,
|
| 258 |
+
-1,
|
| 259 |
+
lineType=cv2.LINE_AA)
|
|
|
|
|
|
|
| 260 |
|
| 261 |
zoomed = cv2.cvtColor(zoomed_bgr, cv2.COLOR_BGR2RGB)
|
| 262 |
|
|
|
|
| 310 |
logger.error(error_msg)
|
| 311 |
logger.error(traceback.format_exc())
|
| 312 |
return None, error_msg
|
|
|
|
| 313 |
def add_formulas_to_template(self, ws, row_pair, col_group, red_font):
|
| 314 |
try:
|
| 315 |
base_col = col_group[1] # Mean column
|
|
|
|
| 326 |
cell1.alignment = openpyxl.styles.Alignment(horizontal='center')
|
| 327 |
|
| 328 |
# CNR Formula for second row
|
| 329 |
+
formula2 = f"=IF(OR({std_col}{row2}=0,{base_col}{row1}=0,{base_col}{row2}=0),0,({base_col}{row1}-{base_col}{row2})/{std_col}{row2})"
|
|
|
|
|
|
|
|
|
|
| 330 |
cell2 = ws[f"{formula_col}{row2}"]
|
| 331 |
cell2.value = formula2
|
| 332 |
cell2.font = red_font
|
|
|
|
| 371 |
(17, 18), (20, 21), (23, 24), (26, 27), (29, 30)
|
| 372 |
]
|
| 373 |
|
| 374 |
+
phantom_sizes = ['(7mm)', '(6.5mm)', '(6mm)', '(5.5mm)', '(5mm)',
|
| 375 |
+
'(4.5mm)', '(4mm)', '(3.5mm)', '(3mm)', '(2.5mm)']
|
|
|
|
|
|
|
| 376 |
|
| 377 |
for i, size in enumerate(phantom_sizes):
|
| 378 |
header_cell = ws.cell(row=row_pairs[i][0]-1, column=1, value=size)
|
|
|
|
| 424 |
stddev_header.alignment = center_alignment
|
| 425 |
current_row += 1
|
| 426 |
|
| 427 |
+
# Calculate StdDev averages for each row pair
|
| 428 |
for i, size in enumerate(phantom_sizes):
|
| 429 |
+
row_number = row_pairs[i][0] # First row only
|
| 430 |
stddev_values = []
|
| 431 |
|
| 432 |
for cols in column_groups:
|
| 433 |
stddev_col = cols[2] # StdDev column
|
| 434 |
cell_value = ws[f"{stddev_col}{row_number}"].value
|
| 435 |
+
if cell_value not in [0, None, '']: # Ignore zeros and empty values
|
| 436 |
stddev_values.append(float(cell_value))
|
| 437 |
|
| 438 |
if stddev_values:
|
|
|
|
| 453 |
cnr_header.alignment = center_alignment
|
| 454 |
current_row += 1
|
| 455 |
|
| 456 |
+
# Calculate CNR averages for each row pair
|
| 457 |
for i, size in enumerate(phantom_sizes):
|
| 458 |
row_number = row_pairs[i][1] # Second row for CNR
|
| 459 |
cnr_references = []
|
|
|
|
| 501 |
logger.error(f"Error saving formatted results: {str(e)}")
|
| 502 |
return None, f"Error saving results: {str(e)}"
|
| 503 |
|
| 504 |
+
|
| 505 |
def _write_result_to_cells(self, ws, result, cols, row):
|
| 506 |
"""Helper method to write a single result to worksheet cells"""
|
| 507 |
center_alignment = openpyxl.styles.Alignment(horizontal='center')
|
|
|
|
| 557 |
self.marks.pop()
|
| 558 |
return self.update_display(), self.format_results()
|
| 559 |
|
|
|
|
| 560 |
def create_interface():
|
| 561 |
print("Creating interface...")
|
| 562 |
analyzer = DicomAnalyzer()
|
|
|
|
| 607 |
- Use Reset All to clear all measurements
|
| 608 |
- Save Formatted Results will create Excel file with formulas
|
| 609 |
""")
|
| 610 |
+
|
| 611 |
def update_diameter(x):
|
| 612 |
analyzer.circle_diameter = float(x)
|
| 613 |
print(f"Diameter updated to: {x}")
|
|
|
|
| 616 |
def save_formatted():
|
| 617 |
output_path = "analysis_results_formatted.xlsx"
|
| 618 |
return analyzer.save_formatted_results(output_path)
|
| 619 |
+
|
| 620 |
# Event handlers
|
| 621 |
file_input.change(
|
| 622 |
fn=analyzer.load_dicom,
|
|
|
|
| 731 |
print(f"Error launching application: {str(e)}")
|
| 732 |
logger.error(f"Error launching application: {str(e)}")
|
| 733 |
logger.error(traceback.format_exc())
|
| 734 |
+
raise e
|