HeshamAI commited on
Commit
db9ee90
·
verified ·
1 Parent(s): 5b1f4dd

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +77 -105
app.py CHANGED
@@ -62,7 +62,6 @@ class DicomAnalyzer:
62
  self.max_pan_y = 0
63
  self.CIRCLE_COLOR = (0, 255, 255) # BGR Yellow
64
  print("DicomAnalyzer initialized...")
65
-
66
  def reset_all(self, image):
67
  self.results = []
68
  self.marks = []
@@ -115,6 +114,7 @@ class DicomAnalyzer:
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
@@ -152,6 +152,60 @@ class DicomAnalyzer:
152
  print(f"Error handling keyboard input: {str(e)}")
153
  return self.display_image
154
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
155
  def analyze_roi(self, evt: gr.SelectData):
156
  try:
157
  if self.current_image is None:
@@ -220,96 +274,6 @@ class DicomAnalyzer:
220
  print(f"Error analyzing ROI: {str(e)}")
221
  return self.display_image, f"Error analyzing ROI: {str(e)}"
222
 
223
- def update_display(self):
224
- try:
225
- if self.original_display is None:
226
- return None
227
-
228
- height, width = self.original_display.shape[:2]
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
-
237
- for x, y, diameter in self.marks:
238
- zoomed_x = int(x * self.zoom_factor)
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
-
263
- self.max_pan_x = max(0, new_width - width)
264
- self.max_pan_y = max(0, new_height - height)
265
- self.pan_x = min(max(0, self.pan_x), self.max_pan_x)
266
- self.pan_y = min(max(0, self.pan_y), self.max_pan_y)
267
-
268
- visible = zoomed[
269
- int(self.pan_y):int(self.pan_y + height),
270
- int(self.pan_x):int(self.pan_x + width)
271
- ]
272
-
273
- return visible
274
- except Exception as e:
275
- print(f"Error updating display: {str(e)}")
276
- return self.original_display
277
-
278
- def save_results(self):
279
- """
280
- Basic save function for raw results with improved error handling and logging
281
- """
282
- try:
283
- if not self.results:
284
- logger.warning("Attempted to save with no results")
285
- return None, "No results to save"
286
-
287
- df = pd.DataFrame(self.results)
288
- columns_order = ['Area (mm²)', 'Mean', 'StdDev', 'Min', 'Max', 'Point']
289
- df = df[columns_order]
290
-
291
- timestamp = time.strftime("%Y%m%d_%H%M%S")
292
- output_file = f"analysis_results_{timestamp}.xlsx"
293
-
294
- with pd.ExcelWriter(output_file, engine='openpyxl') as writer:
295
- df.to_excel(writer, index=False, sheet_name='Results')
296
-
297
- worksheet = writer.sheets['Results']
298
- for idx, col in enumerate(df.columns):
299
- max_length = max(
300
- df[col].astype(str).apply(len).max(),
301
- len(str(col))
302
- ) + 2
303
- worksheet.column_dimensions[get_column_letter(idx + 1)].width = max_length
304
-
305
- logger.info(f"Results saved successfully to {output_file}")
306
- return output_file, f"Results saved successfully to {output_file}"
307
-
308
- except Exception as e:
309
- error_msg = f"Error saving results: {str(e)}"
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
@@ -317,16 +281,16 @@ class DicomAnalyzer:
317
 
318
  row1, row2 = row_pair
319
 
320
- # SNR Formula for first row
321
- formula1 = f"={base_col}{row1}/{std_col}{row1}"
322
  formula_col = get_column_letter(column_index_from_string(col_group[-1]) + 1)
323
  cell1 = ws[f"{formula_col}{row1}"]
324
  cell1.value = formula1
325
  cell1.font = red_font
326
  cell1.alignment = openpyxl.styles.Alignment(horizontal='center')
327
 
328
- # CNR Formula for second row with zero handling
329
- formula2 = f"=IF(OR({std_col}{row2}=0,{base_col}{row1}=0,{base_col}{row2}=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
@@ -458,32 +422,42 @@ class DicomAnalyzer:
458
  row_number = row_pairs[i][1] # Second row for CNR
459
  valid_cnr_refs = []
460
 
 
461
  for cols in column_groups:
462
  formula_col = get_column_letter(column_index_from_string(cols[-1]) + 1)
463
  mean_col = cols[1]
464
  std_col = cols[2]
465
 
466
- # Check if the values used in CNR calculation are valid
467
  mean1 = ws[f"{mean_col}{row_pairs[i][0]}"].value
468
  mean2 = ws[f"{mean_col}{row_pairs[i][1]}"].value
469
  std2 = ws[f"{std_col}{row_pairs[i][1]}"].value
470
 
471
- if (mean1 not in [0, None, ''] and
472
- mean2 not in [0, None, ''] and
473
- std2 not in [0, None, '']):
 
 
 
 
 
 
 
474
  valid_cnr_refs.append(f"{formula_col}{row_number}")
475
 
476
  # Add row to the averages table
477
  size_cell = ws.cell(row=current_row, column=1, value=size)
478
  size_cell.alignment = center_alignment
479
 
480
- avg_cell = ws.cell(row=current_row, column=2)
481
  if valid_cnr_refs:
482
- # Create average formula that ignores blank cells
483
  refs = ",".join(valid_cnr_refs)
484
- avg_cell.value = f'=AVERAGE(IF(LEN({refs})>0,{refs}))'
 
 
 
485
  avg_cell.number_format = '0.000'
486
- avg_cell.alignment = center_alignment
487
 
488
  current_row += 1
489
 
@@ -499,8 +473,6 @@ class DicomAnalyzer:
499
  except Exception as e:
500
  logger.error(f"Error saving formatted results: {str(e)}")
501
  return None, f"Error saving results: {str(e)}"
502
-
503
-
504
  def _write_result_to_cells(self, ws, result, cols, row):
505
  """Helper method to write a single result to worksheet cells"""
506
  center_alignment = openpyxl.styles.Alignment(horizontal='center')
 
62
  self.max_pan_y = 0
63
  self.CIRCLE_COLOR = (0, 255, 255) # BGR Yellow
64
  print("DicomAnalyzer initialized...")
 
65
  def reset_all(self, image):
66
  self.results = []
67
  self.marks = []
 
114
  except Exception as e:
115
  print(f"Error normalizing image: {str(e)}")
116
  return None
117
+
118
  def reset_view(self):
119
  self.zoom_factor = 1.0
120
  self.pan_x = 0
 
152
  print(f"Error handling keyboard input: {str(e)}")
153
  return self.display_image
154
 
155
+ def update_display(self):
156
+ try:
157
+ if self.original_display is None:
158
+ return None
159
+
160
+ height, width = self.original_display.shape[:2]
161
+ new_height = int(height * self.zoom_factor)
162
+ new_width = int(width * self.zoom_factor)
163
+
164
+ zoomed = cv2.resize(self.original_display, (new_width, new_height),
165
+ interpolation=cv2.INTER_CUBIC)
166
+
167
+ zoomed_bgr = cv2.cvtColor(zoomed, cv2.COLOR_RGB2BGR)
168
+
169
+ for x, y, diameter in self.marks:
170
+ zoomed_x = int(x * self.zoom_factor)
171
+ zoomed_y = int(y * self.zoom_factor)
172
+ zoomed_radius = int((diameter/2.0) * self.zoom_factor)
173
+
174
+ cv2.circle(zoomed_bgr,
175
+ (zoomed_x, zoomed_y),
176
+ zoomed_radius,
177
+ self.CIRCLE_COLOR,
178
+ 1,
179
+ lineType=cv2.LINE_AA)
180
+
181
+ num_points = 8
182
+ for i in range(num_points):
183
+ angle = 2 * np.pi * i / num_points
184
+ point_x = int(zoomed_x + zoomed_radius * np.cos(angle))
185
+ point_y = int(zoomed_y + zoomed_radius * np.sin(angle))
186
+ cv2.circle(zoomed_bgr,
187
+ (point_x, point_y),
188
+ 1,
189
+ self.CIRCLE_COLOR,
190
+ -1,
191
+ lineType=cv2.LINE_AA)
192
+
193
+ zoomed = cv2.cvtColor(zoomed_bgr, cv2.COLOR_BGR2RGB)
194
+
195
+ self.max_pan_x = max(0, new_width - width)
196
+ self.max_pan_y = max(0, new_height - height)
197
+ self.pan_x = min(max(0, self.pan_x), self.max_pan_x)
198
+ self.pan_y = min(max(0, self.pan_y), self.max_pan_y)
199
+
200
+ visible = zoomed[
201
+ int(self.pan_y):int(self.pan_y + height),
202
+ int(self.pan_x):int(self.pan_x + width)
203
+ ]
204
+
205
+ return visible
206
+ except Exception as e:
207
+ print(f"Error updating display: {str(e)}")
208
+ return self.original_display
209
  def analyze_roi(self, evt: gr.SelectData):
210
  try:
211
  if self.current_image is None:
 
274
  print(f"Error analyzing ROI: {str(e)}")
275
  return self.display_image, f"Error analyzing ROI: {str(e)}"
276
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
277
  def add_formulas_to_template(self, ws, row_pair, col_group, red_font):
278
  try:
279
  base_col = col_group[1] # Mean column
 
281
 
282
  row1, row2 = row_pair
283
 
284
+ # SNR Formula for first row with error handling
285
+ formula1 = f"=IFERROR({base_col}{row1}/{std_col}{row1},\"\")"
286
  formula_col = get_column_letter(column_index_from_string(col_group[-1]) + 1)
287
  cell1 = ws[f"{formula_col}{row1}"]
288
  cell1.value = formula1
289
  cell1.font = red_font
290
  cell1.alignment = openpyxl.styles.Alignment(horizontal='center')
291
 
292
+ # CNR Formula for second row with error handling
293
+ formula2 = f"=IFERROR(({base_col}{row1}-{base_col}{row2})/{std_col}{row2},\"\")"
294
  cell2 = ws[f"{formula_col}{row2}"]
295
  cell2.value = formula2
296
  cell2.font = red_font
 
422
  row_number = row_pairs[i][1] # Second row for CNR
423
  valid_cnr_refs = []
424
 
425
+ # Get all CNR cells for this row
426
  for cols in column_groups:
427
  formula_col = get_column_letter(column_index_from_string(cols[-1]) + 1)
428
  mean_col = cols[1]
429
  std_col = cols[2]
430
 
431
+ # Check values used in CNR calculation
432
  mean1 = ws[f"{mean_col}{row_pairs[i][0]}"].value
433
  mean2 = ws[f"{mean_col}{row_pairs[i][1]}"].value
434
  std2 = ws[f"{std_col}{row_pairs[i][1]}"].value
435
 
436
+ # Convert values to float if they're strings
437
+ try:
438
+ mean1 = float(mean1) if mean1 not in [None, ''] else 0
439
+ mean2 = float(mean2) if mean2 not in [None, ''] else 0
440
+ std2 = float(std2) if std2 not in [None, ''] else 0
441
+ except (ValueError, TypeError):
442
+ continue
443
+
444
+ # Only skip if ALL values are zero
445
+ if not (mean1 == 0 and mean2 == 0 and std2 == 0):
446
  valid_cnr_refs.append(f"{formula_col}{row_number}")
447
 
448
  # Add row to the averages table
449
  size_cell = ws.cell(row=current_row, column=1, value=size)
450
  size_cell.alignment = center_alignment
451
 
 
452
  if valid_cnr_refs:
453
+ # Create array formula for average that handles errors
454
  refs = ",".join(valid_cnr_refs)
455
+ array_formula = f'=IFERROR(AVERAGE(IF(ISNUMBER({refs}),{refs})),"")'
456
+
457
+ avg_cell = ws.cell(row=current_row, column=2)
458
+ avg_cell.value = array_formula
459
  avg_cell.number_format = '0.000'
460
+ avg_cell.alignment = center_alignment
461
 
462
  current_row += 1
463
 
 
473
  except Exception as e:
474
  logger.error(f"Error saving formatted results: {str(e)}")
475
  return None, f"Error saving results: {str(e)}"
 
 
476
  def _write_result_to_cells(self, ws, result, cols, row):
477
  """Helper method to write a single result to worksheet cells"""
478
  center_alignment = openpyxl.styles.Alignment(horizontal='center')