HeshamAI commited on
Commit
5e4b378
·
verified ·
1 Parent(s): 033b0a1

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +249 -249
app.py CHANGED
@@ -204,265 +204,265 @@ class DicomAnalyzer:
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
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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