HeshamAI commited on
Commit
b9e0532
·
verified ·
1 Parent(s): 7d14829

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +237 -222
app.py CHANGED
@@ -326,7 +326,7 @@ class DicomAnalyzer:
326
  cell1.alignment = openpyxl.styles.Alignment(horizontal='center')
327
 
328
  # CNR Formula for second row
329
- formula2 = f"=({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
@@ -462,11 +462,22 @@ class DicomAnalyzer:
462
  for cols in column_groups:
463
  formula_col = get_column_letter(column_index_from_string(cols[-1]) + 1)
464
  cnr_cell = f"{formula_col}{row_number}"
465
- cnr_references.append(cnr_cell)
 
 
 
 
 
 
 
 
 
 
 
466
 
467
  if cnr_references:
468
- # Create AVERAGE formula using cell references
469
- formula = f"=AVERAGE({','.join(cnr_references)})"
470
  size_cell = ws.cell(row=current_row, column=1, value=size)
471
  size_cell.alignment = center_alignment
472
 
@@ -490,234 +501,238 @@ class DicomAnalyzer:
490
  logger.error(f"Error saving formatted results: {str(e)}")
491
  return None, f"Error saving results: {str(e)}"
492
 
493
- def _write_result_to_cells(self, ws, result, cols, row):
494
- """Helper method to write a single result to worksheet cells"""
495
- center_alignment = openpyxl.styles.Alignment(horizontal='center')
496
-
497
- # Map the headers to the result keys
498
- value_mapping = {
499
- 'Area': 'Area (mm²)',
500
- 'Mean': 'Mean',
501
- 'StdDev': 'StdDev',
502
- 'Min': 'Min',
503
- 'Max': 'Max'
504
- }
505
-
506
- for i, (header, key) in enumerate(value_mapping.items()):
507
- cell = ws[f"{cols[i]}{row}"]
508
- cell.value = float(result[key])
509
- cell.alignment = center_alignment
510
-
511
- def format_results(self):
512
- if not self.results:
513
- return "No measurements yet"
514
- df = pd.DataFrame(self.results)
515
- columns_order = ['Area (mm²)', 'Mean', 'StdDev', 'Min', 'Max', 'Point']
516
- df = df[columns_order]
517
- return df.to_string(index=False)
518
-
519
- def add_blank_row(self, image):
520
- self.results.append({
521
- 'Area (mm²)': '',
522
- 'Mean': '',
523
- 'StdDev': '',
524
- 'Min': '',
525
- 'Max': '',
526
- 'Point': ''
527
- })
528
- return image, self.format_results()
529
-
530
- def add_zero_row(self, image):
531
- self.results.append({
532
- 'Area (mm²)': '0.000',
533
- 'Mean': '0.000',
534
- 'StdDev': '0.000',
535
- 'Min': '0.000',
536
- 'Max': '0.000',
537
- 'Point': '(0, 0)'
538
- })
539
- return image, self.format_results()
540
-
541
- def undo_last(self, image):
542
- if self.results:
543
- self.results.pop()
544
- if self.marks:
545
- self.marks.pop()
546
- return self.update_display(), self.format_results()
547
-
548
- # Interface Creation and Main Execution
549
- def create_interface():
550
- print("Creating interface...")
551
- analyzer = DicomAnalyzer()
552
-
553
- with gr.Blocks(css="#image_display { outline: none; }") as interface:
554
- gr.Markdown("# DICOM Image Analyzer")
555
 
556
- with gr.Row():
557
- with gr.Column():
558
- file_input = gr.File(label="Upload DICOM file")
559
- diameter_slider = gr.Slider(
560
- minimum=1,
561
- maximum=20,
562
- value=9,
563
- step=1,
564
- label="ROI Diameter (pixels)"
565
- )
566
 
567
- with gr.Row():
568
- zoom_in_btn = gr.Button("Zoom In (+)")
569
- zoom_out_btn = gr.Button("Zoom Out (-)")
570
- reset_btn = gr.Button("Reset View")
571
- reset_all_btn = gr.Button("Reset All")
572
-
573
- with gr.Column():
574
- image_display = gr.Image(
575
- label="DICOM Image",
576
- interactive=True,
577
- elem_id="image_display"
578
- )
579
 
580
- with gr.Row():
581
- blank_btn = gr.Button("Add Blank Row")
582
- zero_btn = gr.Button("Add Zero Row")
583
- undo_btn = gr.Button("Undo Last")
584
- save_btn = gr.Button("Save Results")
585
- save_formatted_btn = gr.Button("Save Formatted Results")
586
-
587
- results_display = gr.Textbox(label="Results", interactive=False)
588
- file_output = gr.File(label="Download Results")
589
- key_press = gr.Textbox(visible=False, elem_id="key_press")
590
-
591
- gr.Markdown("""
592
- ### Controls:
593
- - Use arrow keys to pan when zoomed in
594
- - Click points to measure
595
- - Use Zoom In/Out buttons or Reset View to adjust zoom level
596
- - Use Reset All to clear all measurements
597
- - Save Formatted Results will create Excel file with formulas
598
- """)
599
-
600
- def update_diameter(x):
601
- analyzer.circle_diameter = float(x)
602
- print(f"Diameter updated to: {x}")
603
- return f"Diameter set to {x} pixels"
604
-
605
- def save_formatted():
606
- output_path = "analysis_results_formatted.xlsx"
607
- return analyzer.save_formatted_results(output_path)
608
-
609
- # Event handlers
610
- file_input.change(
611
- fn=analyzer.load_dicom,
612
- inputs=file_input,
613
- outputs=[image_display, results_display]
614
- )
615
-
616
- image_display.select(
617
- fn=analyzer.analyze_roi,
618
- outputs=[image_display, results_display]
619
- )
620
 
621
- diameter_slider.change(
622
- fn=update_diameter,
623
- inputs=diameter_slider,
624
- outputs=gr.Textbox(label="Status")
625
- )
 
 
626
 
627
- zoom_in_btn.click(
628
- fn=analyzer.zoom_in,
629
- inputs=image_display,
630
- outputs=image_display,
631
- queue=False
632
- )
 
 
 
 
633
 
634
- zoom_out_btn.click(
635
- fn=analyzer.zoom_out,
636
- inputs=image_display,
637
- outputs=image_display,
638
- queue=False
639
- )
 
 
 
 
640
 
641
- reset_btn.click(
642
- fn=analyzer.reset_view,
643
- outputs=image_display
644
- )
 
 
645
 
646
- reset_all_btn.click(
647
- fn=analyzer.reset_all,
648
- inputs=image_display,
649
- outputs=[image_display, results_display]
650
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
651
 
652
- key_press.change(
653
- fn=analyzer.handle_keyboard,
654
- inputs=key_press,
655
- outputs=image_display
656
- )
657
 
658
- blank_btn.click(
659
- fn=analyzer.add_blank_row,
660
- inputs=image_display,
661
- outputs=[image_display, results_display]
662
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
663
 
664
- zero_btn.click(
665
- fn=analyzer.add_zero_row,
666
- inputs=image_display,
667
- outputs=[image_display, results_display]
668
- )
669
 
670
- undo_btn.click(
671
- fn=analyzer.undo_last,
672
- inputs=image_display,
673
- outputs=[image_display, results_display]
674
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
675
 
676
- save_btn.click(
677
- fn=analyzer.save_results,
678
- outputs=[file_output, results_display]
679
- )
680
-
681
- save_formatted_btn.click(
682
- fn=save_formatted,
683
- outputs=[file_output, results_display]
684
- )
685
-
686
- js = """
687
- <script>
688
- document.addEventListener('keydown', function(e) {
689
- if (['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight'].includes(e.key)) {
690
- e.preventDefault();
691
- const keyPressElement = document.querySelector('#key_press textarea');
692
- if (keyPressElement) {
693
- keyPressElement.value = e.key;
694
- keyPressElement.dispatchEvent(new Event('input'));
695
- }
696
- }
697
- });
698
- </script>
699
- """
700
- gr.HTML(js)
701
-
702
- print("Interface created successfully")
703
- return interface
704
-
705
- if __name__ == "__main__":
706
- try:
707
- print("Starting application...")
708
- interface = create_interface()
709
- print("Launching interface...")
710
- interface.queue()
711
- interface.launch(
712
- server_name="0.0.0.0",
713
- server_port=7860,
714
- share=True,
715
- debug=True,
716
- show_error=True,
717
- quiet=False
718
- )
719
- except Exception as e:
720
- print(f"Error launching application: {str(e)}")
721
- logger.error(f"Error launching application: {str(e)}")
722
- logger.error(traceback.format_exc())
723
- raise e
 
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
 
462
  for cols in column_groups:
463
  formula_col = get_column_letter(column_index_from_string(cols[-1]) + 1)
464
  cnr_cell = f"{formula_col}{row_number}"
465
+
466
+ # Check if the cell would have a valid CNR value
467
+ mean_col = cols[1] # Mean column
468
+ mean1 = ws[f"{mean_col}{row_pairs[i][0]}"].value # First row mean
469
+ mean2 = ws[f"{mean_col}{row_pairs[i][1]}"].value # Second row mean
470
+ stddev2 = ws[f"{cols[2]}{row_pairs[i][1]}"].value # Second row StdDev
471
+
472
+ # Only add to references if values are valid
473
+ if (mean1 not in [0, None, ''] and
474
+ mean2 not in [0, None, ''] and
475
+ stddev2 not in [0, None, '']):
476
+ cnr_references.append(cnr_cell)
477
 
478
  if cnr_references:
479
+ # Create AVERAGE formula excluding zero values
480
+ formula = f"=AVERAGEIF({','.join(cnr_references)},\"<>0\")"
481
  size_cell = ws.cell(row=current_row, column=1, value=size)
482
  size_cell.alignment = center_alignment
483
 
 
501
  logger.error(f"Error saving formatted results: {str(e)}")
502
  return None, f"Error saving results: {str(e)}"
503
 
504
+ except Exception as e:
505
+ logger.error(f"Error saving formatted results: {str(e)}")
506
+ return None, f"Error saving results: {str(e)}"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
507
 
508
+ def _write_result_to_cells(self, ws, result, cols, row):
509
+ """Helper method to write a single result to worksheet cells"""
510
+ center_alignment = openpyxl.styles.Alignment(horizontal='center')
 
 
 
 
 
 
 
511
 
512
+ # Map the headers to the result keys
513
+ value_mapping = {
514
+ 'Area': 'Area (mm²)',
515
+ 'Mean': 'Mean',
516
+ 'StdDev': 'StdDev',
517
+ 'Min': 'Min',
518
+ 'Max': 'Max'
519
+ }
 
 
 
 
520
 
521
+ for i, (header, key) in enumerate(value_mapping.items()):
522
+ cell = ws[f"{cols[i]}{row}"]
523
+ cell.value = float(result[key])
524
+ cell.alignment = center_alignment
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
525
 
526
+ def format_results(self):
527
+ if not self.results:
528
+ return "No measurements yet"
529
+ df = pd.DataFrame(self.results)
530
+ columns_order = ['Area (mm²)', 'Mean', 'StdDev', 'Min', 'Max', 'Point']
531
+ df = df[columns_order]
532
+ return df.to_string(index=False)
533
 
534
+ def add_blank_row(self, image):
535
+ self.results.append({
536
+ 'Area (mm²)': '',
537
+ 'Mean': '',
538
+ 'StdDev': '',
539
+ 'Min': '',
540
+ 'Max': '',
541
+ 'Point': ''
542
+ })
543
+ return image, self.format_results()
544
 
545
+ def add_zero_row(self, image):
546
+ self.results.append({
547
+ 'Area (mm²)': '0.000',
548
+ 'Mean': '0.000',
549
+ 'StdDev': '0.000',
550
+ 'Min': '0.000',
551
+ 'Max': '0.000',
552
+ 'Point': '(0, 0)'
553
+ })
554
+ return image, self.format_results()
555
 
556
+ def undo_last(self, image):
557
+ if self.results:
558
+ self.results.pop()
559
+ if self.marks:
560
+ self.marks.pop()
561
+ return self.update_display(), self.format_results()
562
 
563
+ # Interface Creation and Main Execution
564
+ def create_interface():
565
+ print("Creating interface...")
566
+ analyzer = DicomAnalyzer()
567
+
568
+ with gr.Blocks(css="#image_display { outline: none; }") as interface:
569
+ gr.Markdown("# DICOM Image Analyzer")
570
+
571
+ with gr.Row():
572
+ with gr.Column():
573
+ file_input = gr.File(label="Upload DICOM file")
574
+ diameter_slider = gr.Slider(
575
+ minimum=1,
576
+ maximum=20,
577
+ value=9,
578
+ step=1,
579
+ label="ROI Diameter (pixels)"
580
+ )
581
+
582
+ with gr.Row():
583
+ zoom_in_btn = gr.Button("Zoom In (+)")
584
+ zoom_out_btn = gr.Button("Zoom Out (-)")
585
+ reset_btn = gr.Button("Reset View")
586
+ reset_all_btn = gr.Button("Reset All")
587
+
588
+ with gr.Column():
589
+ image_display = gr.Image(
590
+ label="DICOM Image",
591
+ interactive=True,
592
+ elem_id="image_display"
593
+ )
594
+
595
+ with gr.Row():
596
+ blank_btn = gr.Button("Add Blank Row")
597
+ zero_btn = gr.Button("Add Zero Row")
598
+ undo_btn = gr.Button("Undo Last")
599
+ save_btn = gr.Button("Save Results")
600
+ save_formatted_btn = gr.Button("Save Formatted Results")
601
+
602
+ results_display = gr.Textbox(label="Results", interactive=False)
603
+ file_output = gr.File(label="Download Results")
604
+ key_press = gr.Textbox(visible=False, elem_id="key_press")
605
+
606
+ gr.Markdown("""
607
+ ### Controls:
608
+ - Use arrow keys to pan when zoomed in
609
+ - Click points to measure
610
+ - Use Zoom In/Out buttons or Reset View to adjust zoom level
611
+ - Use Reset All to clear all measurements
612
+ - Save Formatted Results will create Excel file with formulas
613
+ """)
614
+
615
+ def update_diameter(x):
616
+ analyzer.circle_diameter = float(x)
617
+ print(f"Diameter updated to: {x}")
618
+ return f"Diameter set to {x} pixels"
619
 
620
+ def save_formatted():
621
+ output_path = "analysis_results_formatted.xlsx"
622
+ return analyzer.save_formatted_results(output_path)
 
 
623
 
624
+ # Event handlers
625
+ file_input.change(
626
+ fn=analyzer.load_dicom,
627
+ inputs=file_input,
628
+ outputs=[image_display, results_display]
629
+ )
630
+
631
+ image_display.select(
632
+ fn=analyzer.analyze_roi,
633
+ outputs=[image_display, results_display]
634
+ )
635
+
636
+ diameter_slider.change(
637
+ fn=update_diameter,
638
+ inputs=diameter_slider,
639
+ outputs=gr.Textbox(label="Status")
640
+ )
641
+
642
+ zoom_in_btn.click(
643
+ fn=analyzer.zoom_in,
644
+ inputs=image_display,
645
+ outputs=image_display,
646
+ queue=False
647
+ )
648
+
649
+ zoom_out_btn.click(
650
+ fn=analyzer.zoom_out,
651
+ inputs=image_display,
652
+ outputs=image_display,
653
+ queue=False
654
+ )
655
+
656
+ reset_btn.click(
657
+ fn=analyzer.reset_view,
658
+ outputs=image_display
659
+ )
660
+
661
+ reset_all_btn.click(
662
+ fn=analyzer.reset_all,
663
+ inputs=image_display,
664
+ outputs=[image_display, results_display]
665
+ )
666
+
667
+ key_press.change(
668
+ fn=analyzer.handle_keyboard,
669
+ inputs=key_press,
670
+ outputs=image_display
671
+ )
672
+
673
+ blank_btn.click(
674
+ fn=analyzer.add_blank_row,
675
+ inputs=image_display,
676
+ outputs=[image_display, results_display]
677
+ )
678
+
679
+ zero_btn.click(
680
+ fn=analyzer.add_zero_row,
681
+ inputs=image_display,
682
+ outputs=[image_display, results_display]
683
+ )
684
+
685
+ undo_btn.click(
686
+ fn=analyzer.undo_last,
687
+ inputs=image_display,
688
+ outputs=[image_display, results_display]
689
+ )
690
+
691
+ save_btn.click(
692
+ fn=analyzer.save_results,
693
+ outputs=[file_output, results_display]
694
+ )
695
 
696
+ save_formatted_btn.click(
697
+ fn=save_formatted,
698
+ outputs=[file_output, results_display]
699
+ )
 
700
 
701
+ js = """
702
+ <script>
703
+ document.addEventListener('keydown', function(e) {
704
+ if (['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight'].includes(e.key)) {
705
+ e.preventDefault();
706
+ const keyPressElement = document.querySelector('#key_press textarea');
707
+ if (keyPressElement) {
708
+ keyPressElement.value = e.key;
709
+ keyPressElement.dispatchEvent(new Event('input'));
710
+ }
711
+ }
712
+ });
713
+ </script>
714
+ """
715
+ gr.HTML(js)
716
+
717
+ print("Interface created successfully")
718
+ return interface
719
 
720
+ if __name__ == "__main__":
721
+ try:
722
+ print("Starting application...")
723
+ interface = create_interface()
724
+ print("Launching interface...")
725
+ interface.queue()
726
+ interface.launch(
727
+ server_name="0.0.0.0",
728
+ server_port=7860,
729
+ share=True,
730
+ debug=True,
731
+ show_error=True,
732
+ quiet=False
733
+ )
734
+ except Exception as e:
735
+ print(f"Error launching application: {str(e)}")
736
+ logger.error(f"Error launching application: {str(e)}")
737
+ logger.error(traceback.format_exc())
738
+ raise e