HeshamAI commited on
Commit
f42875c
·
verified ·
1 Parent(s): f46e9e2

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +68 -51
app.py CHANGED
@@ -324,8 +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
- SNR => row1
328
- CNR => row2
329
  """
330
  try:
331
  base_col = col_group[1] # Mean column
@@ -333,7 +331,7 @@ class DicomAnalyzer:
333
 
334
  row1, row2 = row_pair
335
 
336
- # SNR formula in row1
337
  formula1 = f"=IFERROR({base_col}{row1}/{std_col}{row1},\"\")"
338
  formula_col = get_column_letter(column_index_from_string(col_group[-1]) + 1)
339
  cell1 = ws[f"{formula_col}{row1}"]
@@ -341,7 +339,7 @@ class DicomAnalyzer:
341
  cell1.font = red_font
342
  cell1.alignment = openpyxl.styles.Alignment(horizontal='center')
343
 
344
- # CNR formula in row2
345
  formula2 = f"=IFERROR(({base_col}{row1}-{base_col}{row2})/{std_col}{row2},\"\")"
346
  cell2 = ws[f"{formula_col}{row2}"]
347
  cell2.value = formula2
@@ -352,11 +350,15 @@ class DicomAnalyzer:
352
  except Exception as e:
353
  logger.error(f"Error adding formulas: {str(e)}")
354
 
 
 
 
355
  def save_formatted_results(self, output_path):
356
  try:
357
  if not self.results:
358
  return None, "No results to save"
359
 
 
360
  wb = openpyxl.Workbook()
361
  ws = wb.active
362
  red_font = openpyxl.styles.Font(color="FF0000")
@@ -373,32 +375,29 @@ class DicomAnalyzer:
373
  ('BV', 'BW', 'BX', 'BY', 'BZ')
374
  ]
375
 
376
- # Write main headers in 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
- # Pairs of rows for measurements
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)
387
  ]
388
 
389
- # Phantom sizes (used for labeling)
390
  phantom_sizes = [
391
  '(7mm)', '(6.5mm)', '(6mm)', '(5.5mm)', '(5mm)',
392
  '(4.5mm)', '(4mm)', '(3.5mm)', '(3mm)', '(2.5mm)'
393
  ]
394
 
395
- # Put phantom size labels in column A (row-1 from the first measurement row)
396
  for i, size in enumerate(phantom_sizes):
397
  header_cell = ws.cell(row=row_pairs[i][0]-1, column=1, value=size)
398
  header_cell.font = red_font
399
  header_cell.alignment = center_alignment
400
 
401
- # Write all self.results into the sheet, as in your original code
402
  result_idx = 0
403
  current_col_group = 0
404
  current_row_pair = 0
@@ -420,7 +419,6 @@ class DicomAnalyzer:
420
  self._write_result_to_cells(ws, result, cols, rows[1])
421
  result_idx += 1
422
 
423
- # Add SNR/CNR formulas for these two rows
424
  self.add_formulas_to_template(ws, rows, cols, red_font)
425
 
426
  current_col_group += 1
@@ -428,7 +426,7 @@ class DicomAnalyzer:
428
  current_col_group = 0
429
  current_row_pair += 1
430
 
431
- # Center-align all non-empty cells from row 2..30 in those columns
432
  for cols in column_groups:
433
  for col in cols:
434
  for row in range(2, 31):
@@ -436,16 +434,24 @@ class DicomAnalyzer:
436
  if cell.value is not None:
437
  cell.alignment = center_alignment
438
 
439
- # Now create the 1-AVG table at row 35..45
 
 
 
 
 
440
  start_row = 35
 
 
441
  ws['C35'] = "1-AVG"
442
  ws['C35'].alignment = center_alignment
443
 
444
- # Merge 3 sets of headers in row 35
445
  ws.merge_cells('D35:E35')
446
  ws.merge_cells('F35:G35')
447
  ws.merge_cells('H35:I35')
448
 
 
449
  avg_headers = {
450
  'D35': 'AVG MEAN',
451
  'F35': 'AVG STDDEV',
@@ -456,74 +462,81 @@ class DicomAnalyzer:
456
  ws[c_ref].font = red_font
457
  ws[c_ref].alignment = center_alignment
458
 
459
- # We will re-use the same 10 phantom sizes
460
  phantom_sizes2 = [
461
  '(7.0mm)', '(6.5mm)', '(6.0mm)', '(5.5mm)', '(5.0mm)',
462
  '(4.5mm)', '(4.0mm)', '(3.5mm)', '(3.0mm)', '(2.5mm)'
463
  ]
464
 
465
- # For each phantom row, read the same row_pairs to gather data
466
  for i, p_size in enumerate(phantom_sizes2):
467
  row = start_row + i + 1 # 36..45
468
 
469
- # Merge each row's 3 sets: (D-E), (F-G), (H-I)
470
  ws.merge_cells(f'D{row}:E{row}')
471
  ws.merge_cells(f'F{row}:G{row}')
472
  ws.merge_cells(f'H{row}:I{row}')
473
 
 
474
  size_cell = ws[f'C{row}']
475
  size_cell.value = p_size
476
  size_cell.font = red_font
477
  size_cell.alignment = center_alignment
478
 
 
 
479
  if i < len(row_pairs):
480
  (raw_row1, raw_row2) = row_pairs[i]
481
  else:
482
- continue
 
 
 
 
 
483
 
484
  mean_values = []
485
  stddev_values = []
486
  cnr_values = []
487
 
488
- # We read from the same column groups.
489
- # But now for CNR, we'll read from the formula column (the one add_formulas_to_template uses).
490
  for group in column_groups:
491
- mean_col = group[1] # e.g. 'C'
492
- std_col = group[2] # e.g. 'D'
493
-
494
- # For MEAN, let's read row1 as before
495
- val_mean = ws[f"{mean_col}{raw_row1}"].value
496
- # For STDDEV, also row1 if you prefer
497
- val_std = ws[f"{std_col}{raw_row1}"].value
498
-
499
- # For CNR, the formula is placed in row2 in the "formula column" after group[-1].
500
- # So let's find that column letter:
501
- formula_col_index = column_index_from_string(group[-1]) + 1
502
- formula_col = get_column_letter(formula_col_index)
503
 
504
- # The formula was put in row2
505
- val_cnr = ws[f"{formula_col}{raw_row2}"].value
 
 
 
506
 
507
- # Convert them to float if possible
508
  try:
509
- val_mean = float(val_mean) if val_mean not in [None, ''] else None
510
- val_std = float(val_std) if val_std not in [None, ''] else None
511
- val_cnr = float(val_cnr) if val_cnr not in [None, ''] else None
512
  except:
513
- val_mean,val_std,val_cnr = None,None,None
514
-
515
- if val_mean is not None:
516
- mean_values.append(val_mean)
517
- if val_std is not None:
518
- stddev_values.append(val_std)
519
- if val_cnr is not None:
520
- cnr_values.append(val_cnr)
521
 
522
- # Compute final averages
523
- final_mean = sum(mean_values)/len(mean_values) if mean_values else None
524
- final_std = sum(stddev_values)/len(stddev_values) if stddev_values else None
525
- final_cnr = sum(cnr_values)/len(cnr_values) if cnr_values else None
 
 
 
 
 
 
 
 
 
 
 
526
 
 
527
  if final_mean is not None:
528
  ws[f'D{row}'].value = final_mean
529
  ws[f'D{row}'].alignment = center_alignment
@@ -539,7 +552,7 @@ class DicomAnalyzer:
539
  ws[f'H{row}'].alignment = center_alignment
540
  ws[f'H{row}'].number_format = '0.0000'
541
 
542
- # Finally, border around C35..I45
543
  thin_side = openpyxl.styles.Side(style='thin')
544
  border = openpyxl.styles.Border(
545
  left=thin_side, right=thin_side, top=thin_side, bottom=thin_side
@@ -553,6 +566,7 @@ class DicomAnalyzer:
553
  except Exception as e:
554
  logger.error(f"Error saving formatted results: {str(e)}")
555
  return None, f"Error saving results: {str(e)}"
 
556
 
557
  def _write_result_to_cells(self, ws, result, cols, row):
558
  center_alignment = openpyxl.styles.Alignment(horizontal='center')
@@ -603,14 +617,17 @@ class DicomAnalyzer:
603
  return image, self.format_results()
604
 
605
  def undo_last(self, image):
606
- if not self.results: # لا توجد نتائج
607
  return self.update_display(), self.format_results()
608
 
609
  last_result = self.results[-1]
 
610
  is_measurement = last_result['Point'] != '(0, 0)'
611
 
 
612
  self.results.pop()
613
 
 
614
  if is_measurement and self.marks:
615
  self.marks.pop()
616
 
 
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
+ # SNR formula
335
  formula1 = f"=IFERROR({base_col}{row1}/{std_col}{row1},\"\")"
336
  formula_col = get_column_letter(column_index_from_string(col_group[-1]) + 1)
337
  cell1 = ws[f"{formula_col}{row1}"]
 
339
  cell1.font = red_font
340
  cell1.alignment = openpyxl.styles.Alignment(horizontal='center')
341
 
342
+ # CNR formula
343
  formula2 = f"=IFERROR(({base_col}{row1}-{base_col}{row2})/{std_col}{row2},\"\")"
344
  cell2 = ws[f"{formula_col}{row2}"]
345
  cell2.value = formula2
 
350
  except Exception as e:
351
  logger.error(f"Error adding formulas: {str(e)}")
352
 
353
+ ########################################################################
354
+ # نفس الدالة الأصلية، لكن نهاية الدالة تضع الجدول في الصف 35 بالشكل المطلوب
355
+ ########################################################################
356
  def save_formatted_results(self, output_path):
357
  try:
358
  if not self.results:
359
  return None, "No results to save"
360
 
361
+ # هنا كل شيء مثل كودك: تنشئ Workbook جديد، تصنع الشيت، تضع الهيدرز...
362
  wb = openpyxl.Workbook()
363
  ws = wb.active
364
  red_font = openpyxl.styles.Font(color="FF0000")
 
375
  ('BV', 'BW', 'BX', 'BY', 'BZ')
376
  ]
377
 
 
378
  for cols in column_groups:
379
  for i, header in enumerate(headers):
380
  cell = ws[f"{cols[i]}1"]
381
  cell.value = header
382
  cell.alignment = center_alignment
383
 
 
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)
387
  ]
388
 
 
389
  phantom_sizes = [
390
  '(7mm)', '(6.5mm)', '(6mm)', '(5.5mm)', '(5mm)',
391
  '(4.5mm)', '(4mm)', '(3.5mm)', '(3mm)', '(2.5mm)'
392
  ]
393
 
394
+ # كتابة أسماء الفانتوم باللون الأحمر في العمود A (قبل الصف الأول)
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
+ # توزيع نتائج ROI في هذه الصفوف والأعمدة كما في كودك
401
  result_idx = 0
402
  current_col_group = 0
403
  current_row_pair = 0
 
419
  self._write_result_to_cells(ws, result, cols, rows[1])
420
  result_idx += 1
421
 
 
422
  self.add_formulas_to_template(ws, rows, cols, red_font)
423
 
424
  current_col_group += 1
 
426
  current_col_group = 0
427
  current_row_pair += 1
428
 
429
+ # ضبط المحاذاة للخلايا التي كُتبت بالفعل
430
  for cols in column_groups:
431
  for col in cols:
432
  for row in range(2, 31):
 
434
  if cell.value is not None:
435
  cell.alignment = center_alignment
436
 
437
+ ################################################################
438
+ # وهنا بدلًا من وضع جداول STDDEV/Mean/CNR منفصلة،
439
+ # نضع الجدول المدمج "1-AVG" من الصف 35 مثل الصورة التي أرسلتها
440
+ ################################################################
441
+
442
+ # سنستخدم نفس فكرة الجدول بالدمج من D35:E35, F35:G35, H35:I35
443
  start_row = 35
444
+
445
+ # ضع عنوان "1-AVG" في C35
446
  ws['C35'] = "1-AVG"
447
  ws['C35'].alignment = center_alignment
448
 
449
+ # ادمج الخلايا الخاصة بالعناوين
450
  ws.merge_cells('D35:E35')
451
  ws.merge_cells('F35:G35')
452
  ws.merge_cells('H35:I35')
453
 
454
+ # ضع العناوين باللون الأحمر
455
  avg_headers = {
456
  'D35': 'AVG MEAN',
457
  'F35': 'AVG STDDEV',
 
462
  ws[c_ref].font = red_font
463
  ws[c_ref].alignment = center_alignment
464
 
465
+ # نفس أسماء الفانتوم
466
  phantom_sizes2 = [
467
  '(7.0mm)', '(6.5mm)', '(6.0mm)', '(5.5mm)', '(5.0mm)',
468
  '(4.5mm)', '(4.0mm)', '(3.5mm)', '(3.0mm)', '(2.5mm)'
469
  ]
470
 
 
471
  for i, p_size in enumerate(phantom_sizes2):
472
  row = start_row + i + 1 # 36..45
473
 
474
+ # ادمج لكل صف: D-E ، F-G ، H-I
475
  ws.merge_cells(f'D{row}:E{row}')
476
  ws.merge_cells(f'F{row}:G{row}')
477
  ws.merge_cells(f'H{row}:I{row}')
478
 
479
+ # اكتب اسم الفانتوم باللون الأحمر في العمود C
480
  size_cell = ws[f'C{row}']
481
  size_cell.value = p_size
482
  size_cell.font = red_font
483
  size_cell.alignment = center_alignment
484
 
485
+ # لكي نحسب الـAVG الحقيقي من الصفوف 2..3 إلخ،
486
+ # نستخدم row_pairs[i] إن أردت. أو كما في المثال الأصلي:
487
  if i < len(row_pairs):
488
  (raw_row1, raw_row2) = row_pairs[i]
489
  else:
490
+ continue # لو عندك 10 فقط
491
+
492
+ # لنفترض أننا نقرأ من نفس الأعمدة اللي فيها mean & std
493
+ # نفترض عندنا على الأقل عمود mean_col = cols[1], stddev_col = cols[2]
494
+ # لكن عندك عدة col groups؛ إذا تريد تجمع من الأول فقط:
495
+ # أو خذ فكرة snippetك السابق:
496
 
497
  mean_values = []
498
  stddev_values = []
499
  cnr_values = []
500
 
501
+ # Example: لو تحب تجمع من نفس الـ column_groups الأولى بس
 
502
  for group in column_groups:
503
+ mean_col = group[1] # 'C','I','O'...الخ
504
+ std_col = group[2] # 'D','J','P'...الخ
 
 
 
 
 
 
 
 
 
 
505
 
506
+ # raw_row1 = الصف الأول
507
+ # raw_row2 = الصف الثاني
508
+ m1 = ws[f"{mean_col}{raw_row1}"].value
509
+ m2 = ws[f"{mean_col}{raw_row2}"].value
510
+ std2 = ws[f"{std_col}{raw_row1}"].value
511
 
 
512
  try:
513
+ m1 = float(m1) if m1 not in [None,''] else None
514
+ m2 = float(m2) if m2 not in [None,''] else None
515
+ std2 = float(std2) if std2 not in [None,''] else None
516
  except:
517
+ m1,m2,std2 = None,None,None
518
+ if (m1 is not None) and (m2 is not None) and (std2 is not None) and (std2!=0):
519
+ mean_values.append(m1)
520
+ stddev_values.append(std2)
521
+ cnr_values.append((m1 - m2)/std2)
 
 
 
522
 
523
+ # الآن نحسب المتوسط
524
+ if len(mean_values) > 0:
525
+ final_mean = sum(mean_values)/len(mean_values)
526
+ else:
527
+ final_mean = None
528
+
529
+ if len(stddev_values) > 0:
530
+ final_std = sum(stddev_values)/len(stddev_values)
531
+ else:
532
+ final_std = None
533
+
534
+ if len(cnr_values) > 0:
535
+ final_cnr = sum(cnr_values)/len(cnr_values)
536
+ else:
537
+ final_cnr = None
538
 
539
+ # نكتبها في الخلايا المدمجة
540
  if final_mean is not None:
541
  ws[f'D{row}'].value = final_mean
542
  ws[f'D{row}'].alignment = center_alignment
 
552
  ws[f'H{row}'].alignment = center_alignment
553
  ws[f'H{row}'].number_format = '0.0000'
554
 
555
+ # أخيراً، ضع الحدود حول C35..I45
556
  thin_side = openpyxl.styles.Side(style='thin')
557
  border = openpyxl.styles.Border(
558
  left=thin_side, right=thin_side, top=thin_side, bottom=thin_side
 
566
  except Exception as e:
567
  logger.error(f"Error saving formatted results: {str(e)}")
568
  return None, f"Error saving results: {str(e)}"
569
+ ########################################################################
570
 
571
  def _write_result_to_cells(self, ws, result, cols, row):
572
  center_alignment = openpyxl.styles.Alignment(horizontal='center')
 
617
  return image, self.format_results()
618
 
619
  def undo_last(self, image):
620
+ if not self.results: # لو مفيش نتائج أصلاً
621
  return self.update_display(), self.format_results()
622
 
623
  last_result = self.results[-1]
624
+ # نتحقق إذا كان آخر إجراء قياس حقيقي أم صف صفري
625
  is_measurement = last_result['Point'] != '(0, 0)'
626
 
627
+ # نمسح آخر نتيجة
628
  self.results.pop()
629
 
630
+ # لو كان قياس حقيقي، نمسح العلامة المقابلة له
631
  if is_measurement and self.marks:
632
  self.marks.pop()
633