Hugging Face
Models
Datasets
Spaces
Community
Docs
Enterprise
Pricing
Log In
Sign Up
Spaces:
mabrokma
/
mindslabmamo
like
0
Running
App
Files
Files
Community
main
mindslabmamo
69.4 kB
1 contributor
History:
5 commits
mabrokma
Can you optimize the veiw of the app and make it looks fancy and allow the four views to be seen together ? - Follow Up Deployment
64c1112
verified
about 1 month ago
.gitattributes
Safe
1.52 kB
initial commit
about 1 month ago
README.md
Safe
214 Bytes
I need a web app that does what this code suppose to do exactly and do in in a nice way import sys import os import json import pydicom import numpy as np from PIL import Image from PyQt6.QtWidgets import ( QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout, QFileDialog, QPushButton, QLabel, QLineEdit, QComboBox, QTextEdit, QFormLayout, QGroupBox, QScrollArea, QSlider, QTableWidget, QTableWidgetItem, QHeaderView, QMessageBox ) from PyQt6.QtCore import Qt from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas from matplotlib.backends.backend_qt5agg import NavigationToolbar2QT as NavigationToolbar from matplotlib.figure import Figure # Main application window class DualViewReporter(QMainWindow): def __init__(self): super().__init__() self.setWindowTitle("Dual-View Mammogram Reporting Tool") self.setGeometry(50, 50, 1800, 1000) # Data storage for two views self.pixel_array_1, self.pixel_array_2 = None, None self.dicom_data_1, self.dicom_data_2 = None, None self.patient_info_loaded = False # --- Main Layout --- main_widget = QWidget() self.setCentralWidget(main_widget) main_layout = QHBoxLayout(main_widget) # --- Create two viewer panels --- viewer_panel_1 = self._create_viewer_widget(view_id=1, title="View 1 (e.g., CC)") viewer_panel_2 = self._create_viewer_widget(view_id=2, title="View 2 (e.g., MLO)") # --- Right Panel: Reporting Form --- form_panel = self._create_reporting_form_widget() # Add widgets to main layout with stretch factors main_layout.addWidget(viewer_panel_1, 2) main_layout.addWidget(viewer_panel_2, 2) main_layout.addWidget(form_panel, 3) def _create_viewer_widget(self, view_id, title): """Creates a complete viewer widget with canvas, toolbar, and controls.""" viewer_widget = QWidget() layout = QVBoxLayout(viewer_widget) # Open File Button open_button = QPushButton(f"Open Image for {title}") open_button.clicked.connect(lambda: self.open_image_file(view_id)) # Matplotlib Figure and Canvas figure = Figure(figsize=(5, 5)) canvas = FigureCanvas(figure) ax = figure.add_subplot(111) ax.set_axis_off() figure.tight_layout() # Matplotlib Navigation Toolbar for Zoom/Pan toolbar = NavigationToolbar(canvas, self) # Sliders level_slider = self._create_slider(lambda: self._update_image_display(view_id)) width_slider = self._create_slider(lambda: self._update_image_display(view_id)) # Store components using setattr to dynamically name them (e.g., self.canvas_1) setattr(self, f'canvas_{view_id}', canvas) setattr(self, f'ax_{view_id}', ax) setattr(self, f'level_slider_{view_id}', level_slider) setattr(self, f'width_slider_{view_id}', width_slider) # Assemble the layout layout.addWidget(open_button) layout.addWidget(toolbar) layout.addWidget(canvas) layout.addWidget(QLabel("Window Level (Brightness)")) layout.addWidget(level_slider) layout.addWidget(QLabel("Window Width (Contrast)")) layout.addWidget(width_slider) return viewer_widget def _create_reporting_form_widget(self): """Creates the entire scrollable reporting form panel.""" form_panel = QScrollArea() form_panel.setWidgetResizable(True) form_widget = QWidget() self.form_layout = QVBoxLayout(form_widget) form_panel.setWidget(form_widget) # Create form sections self._create_patient_info_section() self._create_breast_findings_section("Left Breast", "left") self._create_breast_findings_section("Right Breast", "right") self._create_reasoning_section() self._create_final_recommendation_section() # Save Button self.save_button = QPushButton("Save Report") self.save_button.clicked.connect(self.save_report) self.form_layout.addWidget(self.save_button) return form_panel def open_image_file(self, view_id): """Handles opening an image for a specific viewer.""" file_types = "Image Files (*.dcm *.npy *.png *.jpg *.jpeg *.bmp)" file_name, _ = QFileDialog.getOpenFileName(self, "Open Image File", "", file_types) if not file_name: return _, extension = os.path.splitext(file_name) pixel_array, dicom_data = None, None try: if extension.lower() == '.dcm': dicom_data = pydicom.dcmread(file_name) pixel_array = dicom_data.pixel_array if not self.patient_info_loaded: self._populate_patient_info(dicom_data) self.patient_info_loaded = True elif extension.lower() == '.npy': pixel_array = np.load(file_name) elif extension.lower() in ['.png', '.jpg', '.jpeg', '.bmp']: with Image.open(file_name) as img: pixel_array = np.array(img.convert('L')) else: QMessageBox.warning(self, "Unsupported Format", f"File format '{extension}' is not supported.") return setattr(self, f'pixel_array_{view_id}', pixel_array) setattr(self, f'dicom_data_{view_id}', dicom_data) self._setup_viewer_with_image(view_id) except Exception as e: QMessageBox.critical(self, "Error", f"Failed to load file: {e}") def _populate_patient_info(self, dcm_data): """Fills patient info fields from the first loaded DICOM.""" self.patient_id_input.setText(str(dcm_data.get("PatientID", ""))) self.age_input.setText(str(dcm_data.get("PatientAge", ""))) def _setup_viewer_with_image(self, view_id): """Configures the viewer controls for a newly loaded image.""" pixel_array = getattr(self, f'pixel_array_{view_id}') dicom_data = getattr(self, f'dicom_data_{view_id}') level_slider = getattr(self, f'level_slider_{view_id}') width_slider = getattr(self, f'width_slider_{view_id}') pixel_min, pixel_max = pixel_array.min(), pixel_array.max() if dicom_data: wc = dicom_data.get("WindowCenter", pixel_min + (pixel_max - pixel_min) / 2) ww = dicom_data.get("WindowWidth", pixel_max - pixel_min) if isinstance(wc, pydicom.multival.MultiValue): wc = wc[0] if isinstance(ww, pydicom.multival.MultiValue): ww = ww[0] else: wc = pixel_min + (pixel_max - pixel_min) / 2 ww = pixel_max - pixel_min level_slider.setRange(int(pixel_min), int(pixel_max)) width_slider.setRange(1, int(pixel_max - pixel_min) if pixel_max > pixel_min else 1) level_slider.setValue(int(wc)) width_slider.setValue(int(ww)) self._update_image_display(view_id) def _update_image_display(self, view_id): """Updates a specific viewer's canvas based on its slider values.""" pixel_array = getattr(self, f'pixel_array_{view_id}') if pixel_array is None: return level_slider = getattr(self, f'level_slider_{view_id}') width_slider = getattr(self, f'width_slider_{view_id}') ax = getattr(self, f'ax_{view_id}') canvas = getattr(self, f'canvas_{view_id}') level, width = level_slider.value(), width_slider.value() if width <= 0: width = 1 window_min = level - width / 2 window_max = level + width / 2 img = pixel_array.astype(np.float64) np.clip(img, window_min, window_max, out=img) if window_max > window_min: img = (img - window_min) / (window_max - window_min) else: img.fill(0) ax.clear() ax.imshow(img, cmap='gray') ax.set_axis_off() canvas.draw() def save_report(self): """Gathers all form data and saves it to a JSON file.""" if self.pixel_array_1 is None and self.pixel_array_2 is None: QMessageBox.warning(self, "Warning", "Please open at least one image file before saving.") return report_data = { "patient_info": { "patient_id": self.patient_id_input.text(), "age": self.age_input.text(), "breast_density": self.density_input.currentText() }, "left_breast_findings": { "location": self.left_location.text(), "type": self.left_type.currentText(), "shape": self.left_shape.text(), "margins": self.left_margins.text(), "associated_findings": self.left_assoc.toPlainText(), "initial_impression": self.left_impression.toPlainText(), "recommended_action": self.left_action.currentText(), "birads_category": self.left_birads.currentText(), "rationale": self.left_rationale.toPlainText() }, "right_breast_findings": { "location": self.right_location.text(), "type": self.right_type.currentText(), "shape": self.right_shape.text(), "margins": self.right_margins.text(), "associated_findings": self.right_assoc.toPlainText(), "initial_impression": self.right_impression.toPlainText(), "recommended_action": self.right_action.currentText(), "birads_category": self.right_birads.currentText(), "rationale": self.right_rationale.toPlainText() }, "clinical_reasoning": [], "final_recommendation": { "overall_assessment": self.overall_assessment.currentText(), "recommended_next_step": self.next_step.toPlainText() } } for row in range(self.reasoning_table.rowCount()): items = [self.reasoning_table.item(row, col) for col in range(3)] report_data["clinical_reasoning"].append({ "step": items[0].text() if items[0] else "", "action": items[1].text() if items[1] else "", "reasoning": items[2].text() if items[2] else "" }) file_path, _ = QFileDialog.getSaveFileName(self, "Save Report", "", "JSON Files (*.json)") if file_path: try: with open(file_path, 'w') as f: json.dump(report_data, f, indent=4) QMessageBox.information(self, "Success", f"Report saved successfully to {file_path}") except Exception as e: QMessageBox.critical(self, "Error", f"Failed to save report: {e}") # --- UI Creation Methods (unchanged from previous version) --- def _create_slider(self, on_change): slider = QSlider(Qt.Orientation.Horizontal) slider.valueChanged.connect(on_change) return slider def _create_patient_info_section(self): group_box = QGroupBox("Patient Information") layout = QFormLayout() self.patient_id_input = QLineEdit() self.age_input = QLineEdit() self.density_input = QComboBox() self.density_input.addItems(["", "A: Almost entirely fatty", "B: Scattered areas of fibroglandular density", "C: Heterogeneously dense", "D: Extremely dense"]) layout.addRow("Patient ID:", self.patient_id_input) layout.addRow("Age:", self.age_input) layout.addRow("Breast Density:", self.density_input) group_box.setLayout(layout) self.form_layout.addWidget(group_box) def _create_breast_findings_section(self, title, prefix): group_box = QGroupBox(title) layout = QFormLayout() setattr(self, f"{prefix}_location", QLineEdit()) setattr(self, f"{prefix}_type", QComboBox()) getattr(self, f"{prefix}_type").addItems(["", "Mass", "Calcification", "Asymmetry", "Architectural Distortion", "Other"]) setattr(self, f"{prefix}_shape", QLineEdit()) setattr(self, f"{prefix}_margins", QLineEdit()) setattr(self, f"{prefix}_assoc", QTextEdit()) setattr(self, f"{prefix}_impression", QTextEdit()) setattr(self, f"{prefix}_action", QComboBox()) getattr(self, f"{prefix}_action").addItems(["", "Assign BI-RADS", "Request additional view", "Request ultrasound", "Biopsy", "Short-term follow-up"]) setattr(self, f"{prefix}_birads", QComboBox()) getattr(self, f"{prefix}_birads").addItems(["", "1", "2", "3", "4", "5"]) setattr(self, f"{prefix}_rationale", QTextEdit()) layout.addRow("Lesion Location (M/W/Q):", getattr(self, f"{prefix}_location")) layout.addRow("Lesion Type:", getattr(self, f"{prefix}_type")) layout.addRow("Lesion Shape:", getattr(self, f"{prefix}_shape")) layout.addRow("Lesion Margins:", getattr(self, f"{prefix}_margins")) layout.addRow("Associated Findings:", getattr(self, f"{prefix}_assoc")) layout.addRow("Initial Impression:", getattr(self, f"{prefix}_impression")) layout.addRow("Recommended Action:", getattr(self, f"{prefix}_action")) layout.addRow("Final BI-RADS Category:", getattr(self, f"{prefix}_birads")) layout.addRow("Rationale / Reasoning:", getattr(self, f"{prefix}_rationale")) group_box.setLayout(layout) self.form_layout.addWidget(group_box) def _create_reasoning_section(self): group_box = QGroupBox("Clinical Reasoning Sequence") layout = QVBoxLayout() self.reasoning_table = QTableWidget() self.reasoning_table.setColumnCount(3) self.reasoning_table.setHorizontalHeaderLabels(["Step", "Action Taken", "Reasoning"]) self.reasoning_table.horizontalHeader().setSectionResizeMode(QHeaderView.ResizeMode.Stretch) add_row_button = QPushButton("Add Step") add_row_button.clicked.connect(self.add_reasoning_step) layout.addWidget(self.reasoning_table) layout.addWidget(add_row_button) group_box.setLayout(layout) self.form_layout.addWidget(group_box) def add_reasoning_step(self): row_count = self.reasoning_table.rowCount() self.reasoning_table.insertRow(row_count) step_item = QTableWidgetItem(str(row_count + 1)) step_item.setFlags(step_item.flags() & ~Qt.ItemFlag.ItemIsEditable) self.reasoning_table.setItem(row_count, 0, step_item) def _create_final_recommendation_section(self): group_box = QGroupBox("Final Recommendation") layout = QFormLayout() self.overall_assessment = QComboBox() self.overall_assessment.addItems(["", "Benign", "Suspicion of Malignancy"]) self.next_step = QTextEdit() layout.addRow("Overall Assessment:", self.overall_assessment) layout.addRow("Recommended Next Step:", self.next_step) group_box.setLayout(layout) self.form_layout.addWidget(group_box) # --- Main Execution --- if __name__ == "__main__": app = QApplication(sys.argv) window = DualViewReporter() window.show() sys.exit(app.exec()) - Initial Deployment
about 1 month ago
index.html
Safe
67.3 kB
Can you optimize the veiw of the app and make it looks fancy and allow the four views to be seen together ? - Follow Up Deployment
about 1 month ago
style.css
Safe
388 Bytes
initial commit
about 1 month ago