HeshamAI commited on
Commit
8906f87
·
verified ·
1 Parent(s): 5e4b378

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +251 -250
app.py CHANGED
@@ -203,266 +203,267 @@ class DicomAnalyzer:
203
  except Exception as e:
204
  print(f"Error analyzing ROI: {str(e)}")
205
  return self.display_image, f"Error analyzing ROI: {str(e)}"
206
- def update_display(self):
207
- try:
208
- if self.original_display is None:
209
- return None
210
-
211
- height, width = self.original_display.shape[:2]
212
- new_height = int(height * self.zoom_factor)
213
- new_width = int(width * self.zoom_factor)
214
-
215
- # Create zoomed image
216
- zoomed = cv2.resize(self.original_display, (new_width, new_height),
217
- interpolation=cv2.INTER_CUBIC)
218
-
219
- # Convert to BGR for drawing
220
- zoomed_bgr = cv2.cvtColor(zoomed, cv2.COLOR_RGB2BGR)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
221
 
222
- # Draw marks
223
- for x, y, diameter in self.marks:
224
- zoomed_x = int(x * self.zoom_factor)
225
- zoomed_y = int(y * self.zoom_factor)
226
- zoomed_diameter = int(diameter * self.zoom_factor)
227
-
228
- # Draw main circle in BGR color space
229
- cv2.circle(zoomed_bgr,
230
- (zoomed_x, zoomed_y),
231
- zoomed_diameter // 2,
232
- (0, 255, 255), # BGR: Yellow
233
- 1,
234
- lineType=cv2.LINE_AA)
235
-
236
- # Draw dots on circle
237
- num_points = 8
238
- for i in range(num_points):
239
- angle = 2 * np.pi * i / num_points
240
- point_x = int(zoomed_x + (zoomed_diameter/2) * np.cos(angle))
241
- point_y = int(zoomed_y + (zoomed_diameter/2) * np.sin(angle))
242
- cv2.circle(zoomed_bgr,
243
- (point_x, point_y),
244
- 1,
245
- (0, 255, 255), # BGR: Yellow
246
- -1,
247
- lineType=cv2.LINE_AA)
 
 
248
 
249
- # Convert back to RGB for display
250
- zoomed = cv2.cvtColor(zoomed_bgr, cv2.COLOR_BGR2RGB)
 
 
 
 
251
 
252
- # Calculate pan limits
253
- self.max_pan_x = max(0, new_width - width)
254
- self.max_pan_y = max(0, new_height - height)
255
-
256
- # Ensure pan values are within bounds
257
- self.pan_x = min(max(0, self.pan_x), self.max_pan_x)
258
- self.pan_y = min(max(0, self.pan_y), self.max_pan_y)
259
-
260
- # Extract visible portion
261
- visible = zoomed[
262
- int(self.pan_y):int(self.pan_y + height),
263
- int(self.pan_x):int(self.pan_x + width)
264
- ]
265
 
266
- return visible
267
- except Exception as e:
268
- print(f"Error updating display: {str(e)}")
269
- return self.original_display
270
 
271
- def format_results(self):
272
- if not self.results:
273
- return "No measurements yet"
274
- df = pd.DataFrame(self.results)
275
- columns_order = ['Area (mm²)', 'Mean', 'StdDev', 'Min', 'Max', 'Point']
276
- df = df[columns_order]
277
- return df.to_string(index=False)
278
 
279
- def add_blank_row(self, image):
280
- self.results.append({
281
- 'Area (mm²)': '',
282
- 'Mean': '',
283
- 'StdDev': '',
284
- 'Min': '',
285
- 'Max': '',
286
- 'Point': ''
287
- })
288
- return image, self.format_results()
289
 
290
- def add_zero_row(self, image):
291
- self.results.append({
292
- 'Area (mm²)': '0.000',
293
- 'Mean': '0.000',
294
- 'StdDev': '0.000',
295
- 'Min': '0.000',
296
- 'Max': '0.000',
297
- 'Point': '(0, 0)'
298
- })
299
- return image, self.format_results()
300
 
301
- def undo_last(self, image):
302
- if self.results:
303
- self.results.pop()
304
- if self.marks:
305
- self.marks.pop()
306
- return self.update_display(), self.format_results()
307
 
308
- def save_results(self):
309
- try:
310
- if not self.results:
311
- return None, "No results to save"
312
-
313
- df = pd.DataFrame(self.results)
314
- columns_order = ['Area (mm²)', 'Mean', 'StdDev', 'Min', 'Max', 'Point']
315
- df = df[columns_order]
316
-
317
- temp_file = "analysis_results.xlsx"
318
- df.to_excel(temp_file, index=False)
319
-
320
- return temp_file, "Results saved successfully"
321
- except Exception as e:
322
- return None, f"Error saving results: {str(e)}"
323
 
324
- def create_interface():
325
- print("Creating interface...")
326
- analyzer = DicomAnalyzer()
327
-
328
- with gr.Blocks(css="#image_display { outline: none; }") as interface:
329
- gr.Markdown("# DICOM Image Analyzer")
330
-
331
- with gr.Row():
332
- with gr.Column():
333
- file_input = gr.File(label="Upload DICOM file")
334
- diameter_slider = gr.Slider(
335
- minimum=1,
336
- maximum=20,
337
- value=9,
338
- step=1,
339
- label="ROI Diameter (pixels)"
340
- )
341
-
342
- with gr.Row():
343
- zoom_in_btn = gr.Button("Zoom In (+)")
344
- zoom_out_btn = gr.Button("Zoom Out (-)")
345
- reset_btn = gr.Button("Reset View")
346
-
347
- with gr.Column():
348
- image_display = gr.Image(label="DICOM Image", interactive=True, elem_id="image_display")
349
-
350
- with gr.Row():
351
- blank_btn = gr.Button("Add Blank Row")
352
- zero_btn = gr.Button("Add Zero Row")
353
- undo_btn = gr.Button("Undo Last")
354
- save_btn = gr.Button("Save Results")
355
-
356
- results_display = gr.Textbox(label="Results", interactive=False)
357
- file_output = gr.File(label="Download Results")
358
- key_press = gr.Textbox(visible=False, elem_id="key_press")
359
-
360
- gr.Markdown("""
361
- ### Controls:
362
- - Use arrow keys to pan when zoomed in
363
- - Click points to measure
364
- - Use Zoom In/Out buttons or Reset View to adjust zoom level
365
- """)
366
-
367
- def update_diameter(x):
368
- analyzer.circle_diameter = x
369
- print(f"Diameter updated to: {x}")
370
- return f"Diameter set to {x} pixels"
371
 
372
- # Event handlers
373
- file_input.change(
374
- fn=analyzer.load_dicom,
375
- inputs=file_input,
376
- outputs=[image_display, results_display]
377
- )
378
-
379
- image_display.select(
380
- fn=analyzer.analyze_roi,
381
- outputs=[image_display, results_display]
382
- )
383
-
384
- diameter_slider.change(
385
- fn=update_diameter,
386
- inputs=diameter_slider,
387
- outputs=gr.Textbox(label="Status")
388
- )
389
-
390
- zoom_in_btn.click(
391
- fn=analyzer.zoom_in,
392
- inputs=image_display,
393
- outputs=image_display
394
- )
395
-
396
- zoom_out_btn.click(
397
- fn=analyzer.zoom_out,
398
- inputs=image_display,
399
- outputs=image_display
400
- )
401
-
402
- reset_btn.click(
403
- fn=analyzer.reset_view,
404
- outputs=image_display
405
- )
406
-
407
- key_press.change(
408
- fn=analyzer.handle_keyboard,
409
- inputs=key_press,
410
- outputs=image_display
411
- )
412
-
413
- blank_btn.click(
414
- fn=analyzer.add_blank_row,
415
- inputs=image_display,
416
- outputs=[image_display, results_display]
417
- )
418
-
419
- zero_btn.click(
420
- fn=analyzer.add_zero_row,
421
- inputs=image_display,
422
- outputs=[image_display, results_display]
423
- )
424
-
425
- undo_btn.click(
426
- fn=analyzer.undo_last,
427
- inputs=image_display,
428
- outputs=[image_display, results_display]
429
- )
430
-
431
- save_btn.click(
432
- fn=analyzer.save_results,
433
- outputs=[file_output, results_display]
434
- )
435
 
436
- js = """
437
- <script>
438
- document.addEventListener('keydown', function(e) {
439
- if (['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight'].includes(e.key)) {
440
- e.preventDefault();
441
- const keyPressElement = document.querySelector('#key_press textarea');
442
- if (keyPressElement) {
443
- keyPressElement.value = e.key;
444
- keyPressElement.dispatchEvent(new Event('input'));
445
- }
446
- }
447
- });
448
- </script>
449
- """
450
- gr.HTML(js)
451
-
452
- print("Interface created successfully")
453
- return interface
454
 
455
- if __name__ == "__main__":
456
- try:
457
- print("Starting application...")
458
- interface = create_interface()
459
- print("Launching interface...")
460
- interface.launch(
461
- server_name="0.0.0.0",
462
- server_port=7860,
463
- share=True,
464
- debug=True
465
- )
466
- except Exception as e:
467
- print(f"Error launching application: {str(e)}")
468
- raise e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
203
  except Exception as e:
204
  print(f"Error analyzing ROI: {str(e)}")
205
  return self.display_image, f"Error analyzing ROI: {str(e)}"
206
+
207
+ def update_display(self):
208
+ try:
209
+ if self.original_display is None:
210
+ return None
211
+
212
+ height, width = self.original_display.shape[:2]
213
+ new_height = int(height * self.zoom_factor)
214
+ new_width = int(width * self.zoom_factor)
215
+
216
+ # Create zoomed image
217
+ zoomed = cv2.resize(self.original_display, (new_width, new_height),
218
+ interpolation=cv2.INTER_CUBIC)
219
+
220
+ # Convert to BGR for drawing
221
+ zoomed_bgr = cv2.cvtColor(zoomed, cv2.COLOR_RGB2BGR)
222
+
223
+ # Draw marks
224
+ for x, y, diameter in self.marks:
225
+ zoomed_x = int(x * self.zoom_factor)
226
+ zoomed_y = int(y * self.zoom_factor)
227
+ zoomed_diameter = int(diameter * self.zoom_factor)
228
+
229
+ # Draw main circle in BGR color space
230
+ cv2.circle(zoomed_bgr,
231
+ (zoomed_x, zoomed_y),
232
+ zoomed_diameter // 2,
233
+ (0, 255, 255), # BGR: Yellow
234
+ 1,
235
+ lineType=cv2.LINE_AA)
236
+
237
+ # Draw dots on circle
238
+ num_points = 8
239
+ for i in range(num_points):
240
+ angle = 2 * np.pi * i / num_points
241
+ point_x = int(zoomed_x + (zoomed_diameter/2) * np.cos(angle))
242
+ point_y = int(zoomed_y + (zoomed_diameter/2) * np.sin(angle))
243
+ cv2.circle(zoomed_bgr,
244
+ (point_x, point_y),
245
+ 1,
246
+ (0, 255, 255), # BGR: Yellow
247
+ -1,
248
+ lineType=cv2.LINE_AA)
249
+
250
+ # Convert back to RGB for display
251
+ zoomed = cv2.cvtColor(zoomed_bgr, cv2.COLOR_BGR2RGB)
252
+
253
+ # Calculate pan limits
254
+ self.max_pan_x = max(0, new_width - width)
255
+ self.max_pan_y = max(0, new_height - height)
256
+
257
+ # Ensure pan values are within bounds
258
+ self.pan_x = min(max(0, self.pan_x), self.max_pan_x)
259
+ self.pan_y = min(max(0, self.pan_y), self.max_pan_y)
260
+
261
+ # Extract visible portion
262
+ visible = zoomed[
263
+ int(self.pan_y):int(self.pan_y + height),
264
+ int(self.pan_x):int(self.pan_x + width)
265
+ ]
266
+
267
+ return visible
268
+ except Exception as e:
269
+ print(f"Error updating display: {str(e)}")
270
+ return self.original_display
271
+
272
+ def format_results(self):
273
+ if not self.results:
274
+ return "No measurements yet"
275
+ df = pd.DataFrame(self.results)
276
+ columns_order = ['Area (mm²)', 'Mean', 'StdDev', 'Min', 'Max', 'Point']
277
+ df = df[columns_order]
278
+ return df.to_string(index=False)
279
+
280
+ def add_blank_row(self, image):
281
+ self.results.append({
282
+ 'Area (mm²)': '',
283
+ 'Mean': '',
284
+ 'StdDev': '',
285
+ 'Min': '',
286
+ 'Max': '',
287
+ 'Point': ''
288
+ })
289
+ return image, self.format_results()
290
+
291
+ def add_zero_row(self, image):
292
+ self.results.append({
293
+ 'Area (mm²)': '0.000',
294
+ 'Mean': '0.000',
295
+ 'StdDev': '0.000',
296
+ 'Min': '0.000',
297
+ 'Max': '0.000',
298
+ 'Point': '(0, 0)'
299
+ })
300
+ return image, self.format_results()
301
+
302
+ def undo_last(self, image):
303
+ if self.results:
304
+ self.results.pop()
305
+ if self.marks:
306
+ self.marks.pop()
307
+ return self.update_display(), self.format_results()
308
+
309
+ def save_results(self):
310
+ try:
311
+ if not self.results:
312
+ return None, "No results to save"
313
+
314
+ df = pd.DataFrame(self.results)
315
+ columns_order = ['Area (mm²)', 'Mean', 'StdDev', 'Min', 'Max', 'Point']
316
+ df = df[columns_order]
317
+
318
+ temp_file = "analysis_results.xlsx"
319
+ df.to_excel(temp_file, index=False)
320
+
321
+ return temp_file, "Results saved successfully"
322
+ except Exception as e:
323
+ return None, f"Error saving results: {str(e)}"
324
+
325
+ def create_interface():
326
+ print("Creating interface...")
327
+ analyzer = DicomAnalyzer()
328
+
329
+ with gr.Blocks(css="#image_display { outline: none; }") as interface:
330
+ gr.Markdown("# DICOM Image Analyzer")
331
 
332
+ with gr.Row():
333
+ with gr.Column():
334
+ file_input = gr.File(label="Upload DICOM file")
335
+ diameter_slider = gr.Slider(
336
+ minimum=1,
337
+ maximum=20,
338
+ value=9,
339
+ step=1,
340
+ label="ROI Diameter (pixels)"
341
+ )
342
+
343
+ with gr.Row():
344
+ zoom_in_btn = gr.Button("Zoom In (+)")
345
+ zoom_out_btn = gr.Button("Zoom Out (-)")
346
+ reset_btn = gr.Button("Reset View")
347
+
348
+ with gr.Column():
349
+ image_display = gr.Image(label="DICOM Image", interactive=True, elem_id="image_display")
350
+
351
+ with gr.Row():
352
+ blank_btn = gr.Button("Add Blank Row")
353
+ zero_btn = gr.Button("Add Zero Row")
354
+ undo_btn = gr.Button("Undo Last")
355
+ save_btn = gr.Button("Save Results")
356
+
357
+ results_display = gr.Textbox(label="Results", interactive=False)
358
+ file_output = gr.File(label="Download Results")
359
+ key_press = gr.Textbox(visible=False, elem_id="key_press")
360
 
361
+ gr.Markdown("""
362
+ ### Controls:
363
+ - Use arrow keys to pan when zoomed in
364
+ - Click points to measure
365
+ - Use Zoom In/Out buttons or Reset View to adjust zoom level
366
+ """)
367
 
368
+ def update_diameter(x):
369
+ analyzer.circle_diameter = x
370
+ print(f"Diameter updated to: {x}")
371
+ return f"Diameter set to {x} pixels"
372
+
373
+ # Event handlers
374
+ file_input.change(
375
+ fn=analyzer.load_dicom,
376
+ inputs=file_input,
377
+ outputs=[image_display, results_display]
378
+ )
 
 
379
 
380
+ image_display.select(
381
+ fn=analyzer.analyze_roi,
382
+ outputs=[image_display, results_display]
383
+ )
384
 
385
+ diameter_slider.change(
386
+ fn=update_diameter,
387
+ inputs=diameter_slider,
388
+ outputs=gr.Textbox(label="Status")
389
+ )
 
 
390
 
391
+ zoom_in_btn.click(
392
+ fn=analyzer.zoom_in,
393
+ inputs=image_display,
394
+ outputs=image_display
395
+ )
 
 
 
 
 
396
 
397
+ zoom_out_btn.click(
398
+ fn=analyzer.zoom_out,
399
+ inputs=image_display,
400
+ outputs=image_display
401
+ )
 
 
 
 
 
402
 
403
+ reset_btn.click(
404
+ fn=analyzer.reset_view,
405
+ outputs=image_display
406
+ )
 
 
407
 
408
+ key_press.change(
409
+ fn=analyzer.handle_keyboard,
410
+ inputs=key_press,
411
+ outputs=image_display
412
+ )
 
 
 
 
 
 
 
 
 
 
413
 
414
+ blank_btn.click(
415
+ fn=analyzer.add_blank_row,
416
+ inputs=image_display,
417
+ outputs=[image_display, results_display]
418
+ )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
419
 
420
+ zero_btn.click(
421
+ fn=analyzer.add_zero_row,
422
+ inputs=image_display,
423
+ outputs=[image_display, results_display]
424
+ )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
425
 
426
+ undo_btn.click(
427
+ fn=analyzer.undo_last,
428
+ inputs=image_display,
429
+ outputs=[image_display, results_display]
430
+ )
 
 
 
 
 
 
 
 
 
 
 
 
 
431
 
432
+ save_btn.click(
433
+ fn=analyzer.save_results,
434
+ outputs=[file_output, results_display]
435
+ )
436
+
437
+ js = """
438
+ <script>
439
+ document.addEventListener('keydown', function(e) {
440
+ if (['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight'].includes(e.key)) {
441
+ e.preventDefault();
442
+ const keyPressElement = document.querySelector('#key_press textarea');
443
+ if (keyPressElement) {
444
+ keyPressElement.value = e.key;
445
+ keyPressElement.dispatchEvent(new Event('input'));
446
+ }
447
+ }
448
+ });
449
+ </script>
450
+ """
451
+ gr.HTML(js)
452
+
453
+ print("Interface created successfully")
454
+ return interface
455
+
456
+ if __name__ == "__main__":
457
+ try:
458
+ print("Starting application...")
459
+ interface = create_interface()
460
+ print("Launching interface...")
461
+ interface.launch(
462
+ server_name="0.0.0.0",
463
+ server_port=7860,
464
+ share=True,
465
+ debug=True
466
+ )
467
+ except Exception as e:
468
+ print(f"Error launching application: {str(e)}")
469
+ raise e