HeshamAI commited on
Commit
e3b4edb
·
verified ·
1 Parent(s): 140990e

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +137 -140
app.py CHANGED
@@ -324,7 +324,6 @@ class DicomAnalyzer:
324
  def add_formulas_to_template(self, ws, row_pair, col_group, red_font):
325
  """
326
  Inserts SNR (first row) and CNR (second row) formulas with IFERROR.
327
- (Same approach: col_group[-1] is the last column, so the formula goes in the next col.)
328
  """
329
  try:
330
  base_col = col_group[1] # Mean column
@@ -332,20 +331,21 @@ class DicomAnalyzer:
332
 
333
  row1, row2 = row_pair
334
 
335
- # SNR formula in row1
336
  formula_col = get_column_letter(column_index_from_string(col_group[-1]) + 1)
337
- formula1 = f"=IFERROR({base_col}{row1}/{std_col}{row1},\"\")"
338
- cell1 = ws[f"{formula_col}{row1}"]
339
- cell1.value = formula1
340
- cell1.font = red_font
341
- cell1.alignment = openpyxl.styles.Alignment(horizontal='center')
342
 
343
- # CNR formula in row2
344
- formula2 = f"=IFERROR(({base_col}{row1}-{base_col}{row2})/{std_col}{row2},\"\")"
345
- cell2 = ws[f"{formula_col}{row2}"]
346
- cell2.value = formula2
347
- cell2.font = red_font
348
- cell2.alignment = openpyxl.styles.Alignment(horizontal='center')
 
 
 
 
 
 
 
349
 
350
  logger.debug(f"Added formulas for rows {row1},{row2} in column {formula_col}")
351
  except Exception as e:
@@ -359,7 +359,7 @@ class DicomAnalyzer:
359
  wb = openpyxl.Workbook()
360
  ws = wb.active
361
  red_font = openpyxl.styles.Font(color="FF0000")
362
- center_alignment = openpyxl.styles.Alignment(horizontal='center')
363
 
364
  headers = ['Area', 'Mean', 'StdDev', 'Min', 'Max']
365
 
@@ -372,15 +372,14 @@ class DicomAnalyzer:
372
  ('BJ', 'BK', 'BL', 'BM', 'BN'), ('BP', 'BQ', 'BR', 'BS', 'BT'),
373
  ('BV', 'BW', 'BX', 'BY', 'BZ')
374
  ]
375
-
376
- # Write headers in row1 for each column group
377
  for cols in column_groups:
378
  for i, header in enumerate(headers):
379
  cell = ws[f"{cols[i]}1"]
380
  cell.value = header
381
  cell.alignment = center_alignment
382
 
383
- # row pairs for 10 phantoms, each has row1 and row2
384
  row_pairs = [
385
  (2, 3), (5, 6), (8, 9), (11, 12), (14, 15),
386
  (17, 18), (20, 21), (23, 24), (26, 27), (29, 30)
@@ -391,13 +390,13 @@ class DicomAnalyzer:
391
  '(4.5mm)', '(4mm)', '(3.5mm)', '(3mm)', '(2.5mm)'
392
  ]
393
 
394
- # Put phantom sizes in column A (above row1 for each pair)
395
  for i, size in enumerate(phantom_sizes):
396
  header_cell = ws.cell(row=row_pairs[i][0]-1, column=1, value=size)
397
  header_cell.font = red_font
398
  header_cell.alignment = center_alignment
399
 
400
- # Fill data from self.results in these row_pairs/column_groups
401
  result_idx = 0
402
  current_col_group = 0
403
  current_row_pair = 0
@@ -407,29 +406,26 @@ class DicomAnalyzer:
407
  break
408
 
409
  cols = column_groups[current_col_group]
410
- rows = row_pairs[current_row_pair]
411
 
412
- # first measurement -> row1
413
  if result_idx < len(self.results):
414
  result = self.results[result_idx]
415
- self._write_result_to_cells(ws, result, cols, rows[0])
416
  result_idx += 1
417
 
418
- # second measurement -> row2
419
  if result_idx < len(self.results):
420
  result = self.results[result_idx]
421
- self._write_result_to_cells(ws, result, cols, rows[1])
422
  result_idx += 1
423
 
424
- # Insert SNR/CNR formulas in these 2 rows (mean, std dev columns to formula col)
425
- self.add_formulas_to_template(ws, rows, cols, red_font)
426
 
427
  current_col_group += 1
428
  if current_col_group >= len(column_groups):
429
  current_col_group = 0
430
  current_row_pair += 1
431
 
432
- # center alignment for existing cells (2..30 in all column groups)
433
  for cols in column_groups:
434
  for col in cols:
435
  for row in range(2, 31):
@@ -437,128 +433,129 @@ class DicomAnalyzer:
437
  if cell.value is not None:
438
  cell.alignment = center_alignment
439
 
440
- # Now do the 3 sections: StdDev Averages, Mean Averages, CNR Averages
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
441
 
442
- ##################
443
- # 1) StdDev Averages
444
- ##################
445
- current_row = 32
446
- stddev_header = ws.cell(row=current_row, column=1, value="StdDev Averages")
447
- stddev_header.font = red_font
448
- stddev_header.alignment = center_alignment
449
- current_row += 1
450
-
451
- for i, size in enumerate(phantom_sizes):
452
- row_number = row_pairs[i][0]
453
- stddev_values = []
454
-
455
- for cols in column_groups:
456
- stddev_col = cols[2] # 'D','J','P'.. etc.
457
- cell_value = ws[f"{stddev_col}{row_number}"].value
458
- if cell_value not in [0, None, '']:
459
- stddev_values.append(float(cell_value))
460
-
461
- size_cell = ws.cell(row=current_row, column=1, value=size)
462
- size_cell.alignment = center_alignment
463
-
464
- if stddev_values:
465
- avg_stddev = sum(stddev_values) / len(stddev_values)
466
- avg_cell = ws.cell(row=current_row, column=2, value=avg_stddev)
467
- avg_cell.number_format = '0.000'
468
- avg_cell.alignment = center_alignment
469
- current_row += 1
470
-
471
- ##################
472
- # 2) Mean Averages
473
- ##################
474
- current_row += 2
475
- mean_header = ws.cell(row=current_row, column=1, value="Mean Averages")
476
- mean_header.font = red_font
477
- mean_header.alignment = center_alignment
478
- current_row += 1
479
 
480
- for i, size in enumerate(phantom_sizes):
481
- row_number = row_pairs[i][0] # we take the first row only (object)
482
  mean_values = []
483
-
484
- for cols in column_groups:
485
- mean_col = cols[1] # 'C','I','O'.. etc.
486
- cell_value = ws[f"{mean_col}{row_number}"].value
487
- if cell_value not in [0, None, '']:
488
- mean_values.append(float(cell_value))
489
-
490
- size_cell = ws.cell(row=current_row, column=1, value=size)
491
- size_cell.alignment = center_alignment
492
-
493
- if mean_values:
494
- avg_mean = sum(mean_values) / len(mean_values)
495
- avg_cell = ws.cell(row=current_row, column=2, value=avg_mean)
496
- avg_cell.number_format = '0.000'
497
- avg_cell.alignment = center_alignment
498
- current_row += 1
499
-
500
- current_row += 2
501
-
502
- ##################
503
- # 3) CNR Averages (the snippet approach: build `=AVERAGE(...)` referencing each phantom's CNR cell)
504
- ##################
505
- cnr_header = ws.cell(row=current_row, column=1, value="CNR Averages")
506
- cnr_header.font = red_font
507
- cnr_header.alignment = center_alignment
508
- current_row += 1
509
-
510
- for i, size in enumerate(phantom_sizes):
511
- row_number = row_pairs[i][1] # second row for each phantom
512
- cnr_cells = []
513
-
514
- # We'll collect references to the formula column for each column group and row2.
515
- for cols in column_groups:
516
- # formula col is after the last col in group (like 'F' -> 'G')
517
- formula_col = get_column_letter(column_index_from_string(cols[-1]) + 1)
518
- cnr_cell_ref = f"{formula_col}{row_number}"
519
-
520
- # we also check if mean1,mean2,std2 are zero => skip or not. This is from the snippet logic.
521
- mean_col = cols[1]
522
- std_col = cols[2]
523
-
524
- mean1_val = ws[f"{mean_col}{row_pairs[i][0]}"].value
525
- mean2_val = ws[f"{mean_col}{row_pairs[i][1]}"].value
526
- std2_val = ws[f"{std_col}{row_pairs[i][1]}"].value
527
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
528
  try:
529
- mean1_val = float(mean1_val) if mean1_val not in [None, ''] else 0
530
- mean2_val = float(mean2_val) if mean2_val not in [None, ''] else 0
531
- std2_val = float(std2_val) if std2_val not in [None, ''] else 0
 
 
532
  except:
533
- mean1_val, mean2_val, std2_val = 0, 0, 0
534
 
535
- if not (mean1_val == 0 and mean2_val == 0 and std2_val == 0):
536
- # i.e. valid ROI data => we add the formula cell reference to average later.
537
  cnr_cells.append(cnr_cell_ref)
538
-
539
- size_cell = ws.cell(row=current_row, column=1, value=size)
540
- size_cell.alignment = center_alignment
541
 
542
- if cnr_cells:
543
- # create a new formula to average those CNR cells:
544
- average_formula = f'=IFERROR(AVERAGE({",".join(cnr_cells)}), "")'
545
- avg_cell = ws.cell(row=current_row, column=2)
546
- avg_cell.value = average_formula
547
- avg_cell.number_format = '0.000'
548
- avg_cell.alignment = center_alignment
549
-
550
- current_row += 1
551
 
552
- # Finally, center-align from row 32.. up to current_row in columns 1..2
553
- for row in range(32, current_row):
554
- for col in range(1, 3):
555
- cell = ws.cell(row=row, column=col)
556
- cell.alignment = center_alignment
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
557
 
558
- # Save workbook to disk
559
  wb.save(output_path)
560
  return output_path, f"Results saved successfully ({len(self.results)} measurements)"
561
-
562
  except Exception as e:
563
  logger.error(f"Error saving formatted results: {str(e)}")
564
  return None, f"Error saving results: {str(e)}"
@@ -612,12 +609,11 @@ class DicomAnalyzer:
612
  return image, self.format_results()
613
 
614
  def undo_last(self, image):
615
- if not self.results: # لو مفيش نتائج أصلاً
616
  return self.update_display(), self.format_results()
617
 
618
  last_result = self.results[-1]
619
- is_measurement = last_result['Point'] != '(0, 0)'
620
-
621
  self.results.pop()
622
 
623
  if is_measurement and self.marks:
@@ -675,7 +671,7 @@ def create_interface():
675
  - Use Zoom In/Out buttons or Reset View to adjust zoom level.
676
  - Use Reset All to clear all measurements.
677
  - "Save Results": basic Excel with raw data.
678
- - "Save Formatted Results": Excel with advanced formatting & formulas (including average CNR).
679
  """)
680
 
681
  def update_diameter(x):
@@ -763,6 +759,7 @@ def create_interface():
763
  outputs=[file_output, results_display]
764
  )
765
 
 
766
  js = """
767
  <script>
768
  document.addEventListener('keydown', function(e) {
 
324
  def add_formulas_to_template(self, ws, row_pair, col_group, red_font):
325
  """
326
  Inserts SNR (first row) and CNR (second row) formulas with IFERROR.
 
327
  """
328
  try:
329
  base_col = col_group[1] # Mean column
 
331
 
332
  row1, row2 = row_pair
333
 
 
334
  formula_col = get_column_letter(column_index_from_string(col_group[-1]) + 1)
 
 
 
 
 
335
 
336
+ # SNR formula -> row1
337
+ formula_snr = f"=IFERROR({base_col}{row1}/{std_col}{row1},\"\")"
338
+ cell_snr = ws[f"{formula_col}{row1}"]
339
+ cell_snr.value = formula_snr
340
+ cell_snr.font = red_font
341
+ cell_snr.alignment = openpyxl.styles.Alignment(horizontal='center')
342
+
343
+ # CNR formula -> row2
344
+ formula_cnr = f"=IFERROR(({base_col}{row1}-{base_col}{row2})/{std_col}{row2},\"\")"
345
+ cell_cnr = ws[f"{formula_col}{row2}"]
346
+ cell_cnr.value = formula_cnr
347
+ cell_cnr.font = red_font
348
+ cell_cnr.alignment = openpyxl.styles.Alignment(horizontal='center')
349
 
350
  logger.debug(f"Added formulas for rows {row1},{row2} in column {formula_col}")
351
  except Exception as e:
 
359
  wb = openpyxl.Workbook()
360
  ws = wb.active
361
  red_font = openpyxl.styles.Font(color="FF0000")
362
+ center_alignment = openpyxl.styles.Alignment(horizontal='center', vertical='center')
363
 
364
  headers = ['Area', 'Mean', 'StdDev', 'Min', 'Max']
365
 
 
372
  ('BJ', 'BK', 'BL', 'BM', 'BN'), ('BP', 'BQ', 'BR', 'BS', 'BT'),
373
  ('BV', 'BW', 'BX', 'BY', 'BZ')
374
  ]
375
+
376
+ # Write the headers in row1 for each column group.
377
  for cols in column_groups:
378
  for i, header in enumerate(headers):
379
  cell = ws[f"{cols[i]}1"]
380
  cell.value = header
381
  cell.alignment = center_alignment
382
 
 
383
  row_pairs = [
384
  (2, 3), (5, 6), (8, 9), (11, 12), (14, 15),
385
  (17, 18), (20, 21), (23, 24), (26, 27), (29, 30)
 
390
  '(4.5mm)', '(4mm)', '(3.5mm)', '(3mm)', '(2.5mm)'
391
  ]
392
 
393
+ # Put phantom size labels in column A above each row pair.
394
  for i, size in enumerate(phantom_sizes):
395
  header_cell = ws.cell(row=row_pairs[i][0]-1, column=1, value=size)
396
  header_cell.font = red_font
397
  header_cell.alignment = center_alignment
398
 
399
+ # Write the results in the row pairs, add SNR/CNR formulas.
400
  result_idx = 0
401
  current_col_group = 0
402
  current_row_pair = 0
 
406
  break
407
 
408
  cols = column_groups[current_col_group]
409
+ row1, row2 = row_pairs[current_row_pair]
410
 
 
411
  if result_idx < len(self.results):
412
  result = self.results[result_idx]
413
+ self._write_result_to_cells(ws, result, cols, row1)
414
  result_idx += 1
415
 
 
416
  if result_idx < len(self.results):
417
  result = self.results[result_idx]
418
+ self._write_result_to_cells(ws, result, cols, row2)
419
  result_idx += 1
420
 
421
+ self.add_formulas_to_template(ws, (row1,row2), cols, red_font)
 
422
 
423
  current_col_group += 1
424
  if current_col_group >= len(column_groups):
425
  current_col_group = 0
426
  current_row_pair += 1
427
 
428
+ # Center-align the raw data rows (2..30) in all column groups.
429
  for cols in column_groups:
430
  for col in cols:
431
  for row in range(2, 31):
 
433
  if cell.value is not None:
434
  cell.alignment = center_alignment
435
 
436
+ #########################################################
437
+ # تصميم "1-AVG" في الصف 35 (مدمج D35:E35, F35:G35, H35:I35)
438
+ #########################################################
439
+ start_row = 35
440
+ ws['C35'] = "1-AVG"
441
+ ws['C35'].alignment = center_alignment
442
+
443
+ ws.merge_cells('D35:E35')
444
+ ws.merge_cells('F35:G35')
445
+ ws.merge_cells('H35:I35')
446
+
447
+ headers_avg = {
448
+ 'D35': 'AVG MEAN',
449
+ 'F35': 'AVG STDDEV',
450
+ 'H35': 'AVG CNR'
451
+ }
452
+ for c_ref, text_val in headers_avg.items():
453
+ ws[c_ref] = text_val
454
+ ws[c_ref].font = red_font
455
+ ws[c_ref].alignment = center_alignment
456
+
457
+ # We'll keep the same 10 phantom sizes, to fill rows 36..45.
458
+ phantom_sizes2 = [
459
+ '(7.0mm)', '(6.5mm)', '(6.0mm)', '(5.5mm)', '(5.0mm)',
460
+ '(4.5mm)', '(4.0mm)', '(3.5mm)', '(3.0mm)', '(2.5mm)'
461
+ ]
462
 
463
+ for i, size_label in enumerate(phantom_sizes2):
464
+ row = start_row + i + 1 # 36..45
465
+
466
+ # merge cells for each row (D-E, F-G, H-I)
467
+ ws.merge_cells(f'D{row}:E{row}')
468
+ ws.merge_cells(f'F{row}:G{row}')
469
+ ws.merge_cells(f'H{row}:I{row}')
470
+
471
+ c_cell = ws[f'C{row}']
472
+ c_cell.value = size_label
473
+ c_cell.font = red_font
474
+ c_cell.alignment = center_alignment
475
+
476
+ # We'll read from row_pairs[i]: row1->(2,5,8..), row2->(3,6,9..)
477
+ if i >= len(row_pairs):
478
+ continue
479
+ (raw_row1, raw_row2) = row_pairs[i]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
480
 
 
 
481
  mean_values = []
482
+ stddev_values = []
483
+ cnr_cells = [] # We'll collect references to the formula column for row2.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
484
 
485
+ # for each column group, read Mean (row1), StdDev (row1) if you want to average them.
486
+ # plus the CNR formula in row2.
487
+ for group in column_groups:
488
+ mean_col = group[1] # e.g. 'C'
489
+ std_col = group[2] # e.g. 'D'
490
+
491
+ m1_val = ws[f"{mean_col}{raw_row1}"].value
492
+ if m1_val not in [None,'']:
493
+ try:
494
+ mean_values.append(float(m1_val))
495
+ except:
496
+ pass
497
+
498
+ std1_val = ws[f"{std_col}{raw_row1}"].value
499
+ if std1_val not in [None,'']:
500
+ try:
501
+ stddev_values.append(float(std1_val))
502
+ except:
503
+ pass
504
+
505
+ # The formula col is after group[-1]
506
+ formula_col = get_column_letter(column_index_from_string(group[-1]) + 1)
507
+ cnr_cell_ref = f"{formula_col}{raw_row2}"
508
+
509
+ # Check if row2 is not zero => basically the snippet's logic:
510
  try:
511
+ mean2_val = ws[f"{mean_col}{raw_row2}"].value
512
+ std2_val = ws[f"{std_col}{raw_row2}"].value
513
+
514
+ mean2_val = float(mean2_val) if mean2_val not in [None,''] else 0
515
+ std2_val = float(std2_val) if std2_val not in [None,''] else 0
516
  except:
517
+ mean2_val, std2_val = 0,0
518
 
519
+ if not (mean2_val == 0 and std2_val == 0):
520
+ # i.e. there's valid data => we use that formula cell.
521
  cnr_cells.append(cnr_cell_ref)
 
 
 
522
 
523
+ # Compute average MEAN, STDDEV in python from row1 if you want.
524
+ final_mean = sum(mean_values)/len(mean_values) if mean_values else None
525
+ final_std = sum(stddev_values)/len(stddev_values) if stddev_values else None
 
 
 
 
 
 
526
 
527
+ # For CNR, we build a formula =AVERAGE(...) referencing all the row2 formula cells.
528
+ # But we'll do it as an Excel formula:
529
+ # e.g. =IFERROR(AVERAGE(G3, G6, ...),"")
530
+
531
+ if final_mean is not None:
532
+ ws[f'D{row}'].value = final_mean
533
+ ws[f'D{row}'].alignment = center_alignment
534
+ ws[f'D{row}'].number_format = '0.0000'
535
+
536
+ if final_std is not None:
537
+ ws[f'F{row}'].value = final_std
538
+ ws[f'F{row}'].alignment = center_alignment
539
+ ws[f'F{row}'].number_format = '0.0000'
540
+
541
+ if cnr_cells:
542
+ # create a formula for average of those CNR references.
543
+ formula_avg_cnr = f"=IFERROR(AVERAGE({','.join(cnr_cells)}),\"\")"
544
+ ws[f'H{row}'].value = formula_avg_cnr
545
+ ws[f'H{row}'].alignment = center_alignment
546
+ ws[f'H{row}'].number_format = '0.0000'
547
+
548
+ # Add thin border around the region C35..I45.
549
+ thin_side = openpyxl.styles.Side(style='thin')
550
+ border = openpyxl.styles.Border(
551
+ left=thin_side, right=thin_side, top=thin_side, bottom=thin_side
552
+ )
553
+ for r in range(35, 46):
554
+ for col in ['C','D','E','F','G','H','I']:
555
+ ws[f"{col}{r}"].border = border
556
 
 
557
  wb.save(output_path)
558
  return output_path, f"Results saved successfully ({len(self.results)} measurements)"
 
559
  except Exception as e:
560
  logger.error(f"Error saving formatted results: {str(e)}")
561
  return None, f"Error saving results: {str(e)}"
 
609
  return image, self.format_results()
610
 
611
  def undo_last(self, image):
612
+ if not self.results: # لا توجد نتائج
613
  return self.update_display(), self.format_results()
614
 
615
  last_result = self.results[-1]
616
+ is_measurement = (last_result['Point'] != '(0, 0)')
 
617
  self.results.pop()
618
 
619
  if is_measurement and self.marks:
 
671
  - Use Zoom In/Out buttons or Reset View to adjust zoom level.
672
  - Use Reset All to clear all measurements.
673
  - "Save Results": basic Excel with raw data.
674
+ - "Save Formatted Results": Excel with advanced formatting & formulas.
675
  """)
676
 
677
  def update_diameter(x):
 
759
  outputs=[file_output, results_display]
760
  )
761
 
762
+ # JavaScript to capture arrow keys and pass to Gradio.
763
  js = """
764
  <script>
765
  document.addEventListener('keydown', function(e) {