Skip to content
Snippets Groups Projects
Commit 6bcc3a83 authored by s224361's avatar s224361
Browse files

Cleaned up modules

parent 4cfed8cb
No related branches found
No related tags found
No related merge requests found
import math
import numpy as np
from scipy.signal import savgol_filter
from PyQt5.QtWidgets import (
QMainWindow, QPushButton, QHBoxLayout,
QVBoxLayout, QWidget, QFileDialog
)
from PyQt5.QtGui import QPixmap, QImage
from PyQt5.QtCore import QCloseEvent
from compute_cost_image import compute_cost_image
from preprocess_image import preprocess_image
from advancedSettingsWidget import AdvancedSettingsWidget
......@@ -21,28 +21,26 @@ class MainWindow(QMainWindow):
self._circle_calibrated_radius = 6
self._last_loaded_file_path = None
# For the contrast slider
# Value for the contrast slider
self._current_clip_limit = 0.01
# Outer widget + layout
# Outer widget and layout
self._main_widget = QWidget()
self._main_layout = QHBoxLayout(self._main_widget)
# The "left" part: container for the image area + its controls
# Container for the image area and its controls
self._left_panel = QVBoxLayout()
# We'll make a container widget for the left panel, so we can set stretches:
# Container widget for stretching the panel
self._left_container = QWidget()
self._left_container.setLayout(self._left_panel)
# Now we add them to the main layout with 70%:30% ratio
self._main_layout.addWidget(self._left_container, 7) # 70%
self._main_layout.addWidget(self._left_container, 7) # 70% ratio of the full window
# We haven't added the advanced widget yet, but we'll do so with ratio=3 => 30%
# Advanced widget window
self._advanced_widget = AdvancedSettingsWidget(self)
# Hide it initially
self._advanced_widget.hide()
self._main_layout.addWidget(self._advanced_widget, 3)
self._main_layout.addWidget(self._advanced_widget, 3) # 30% ratio of the full window
self.setCentralWidget(self._main_widget)
......@@ -64,7 +62,6 @@ class MainWindow(QMainWindow):
self.btn_clear_points.clicked.connect(self.clear_points)
btn_layout.addWidget(self.btn_clear_points)
# "Advanced Settings" toggle
self.btn_advanced = QPushButton("Advanced Settings")
self.btn_advanced.setCheckable(True)
self.btn_advanced.clicked.connect(self._toggle_advanced_settings)
......@@ -76,7 +73,10 @@ class MainWindow(QMainWindow):
self._old_central_widget = None
self._editor = None
def _toggle_advanced_settings(self, checked):
def _toggle_advanced_settings(self, checked: bool):
"""
Toggles the visibility of the advanced settings widget.
"""
if checked:
self._advanced_widget.show()
else:
......@@ -84,8 +84,11 @@ class MainWindow(QMainWindow):
# Force re-layout
self.adjustSize()
def open_circle_editor(self):
""" Replace central widget with circle editor. """
"""
Replace central widget with circle editor.
"""
if not self._last_loaded_pixmap:
print("No image loaded yet! Cannot open circle editor.")
return
......@@ -102,10 +105,17 @@ class MainWindow(QMainWindow):
self._editor = editor
self.setCentralWidget(editor)
def _on_circle_editor_done(self, final_radius):
def _on_circle_editor_done(self, final_radius: int):
"""
Updates the calibrated radius, computes the cost image based on the new radius,
and updates the image view with the new cost image.
It also restores the previous central widget and cleans up the editor widget.
"""
self._circle_calibrated_radius = final_radius
print(f"Circle Editor done. Radius = {final_radius}")
# Update cost image and path using new radius
if self._last_loaded_file_path:
cost_img = compute_cost_image(
self._last_loaded_file_path,
......@@ -118,6 +128,7 @@ class MainWindow(QMainWindow):
self.image_view._rebuild_full_path()
self._update_advanced_images()
# Swap back to central widget
editor_widget = self.takeCentralWidget()
if editor_widget is not None:
editor_widget.setParent(None)
......@@ -131,15 +142,23 @@ class MainWindow(QMainWindow):
self._editor = None
def toggle_rainbow(self):
"""
Toggle rainbow coloring of the path.
"""
self.image_view.toggle_rainbow()
def load_image(self):
"""
Load an image and update the image view and cost image.
The supported image formats are: PNG, JPG, JPEG, BMP, and TIF.
"""
options = QFileDialog.Options()
file_path, _ = QFileDialog.getOpenFileName(
self, "Open Image", "",
"Images (*.png *.jpg *.jpeg *.bmp *.tif)",
options=options
)
if file_path:
self.image_view.load_image(file_path)
......@@ -158,7 +177,10 @@ class MainWindow(QMainWindow):
self._last_loaded_file_path = file_path
self._update_advanced_images()
def update_contrast(self, clip_limit):
def update_contrast(self, clip_limit: float):
"""
Updates and applies the contrast value of the image.
"""
self._current_clip_limit = clip_limit
if self._last_loaded_file_path:
cost_img = compute_cost_image(
......@@ -174,6 +196,10 @@ class MainWindow(QMainWindow):
self._update_advanced_images()
def _update_advanced_images(self):
"""
Updates the advanced images display with the latest image.
If no image has been loaded, the method returns without making any updates.
"""
if not self._last_loaded_pixmap:
return
pm_np = self._qpixmap_to_gray_float(self._last_loaded_pixmap)
......@@ -185,7 +211,16 @@ class MainWindow(QMainWindow):
cost_img_np = self.image_view.cost_image
self._advanced_widget.update_displays(contrasted_blurred, cost_img_np)
def _qpixmap_to_gray_float(self, qpix):
def _qpixmap_to_gray_float(self, qpix: QPixmap) -> np.ndarray:
"""
Convert a QPixmap to a grayscale float array.
Args:
qpix: The QPixmap to be converted.
Returns:
A 2D numpy array representing the grayscale image.
"""
img = qpix.toImage()
img = img.convertToFormat(QImage.Format_ARGB32)
ptr = img.bits()
......@@ -205,12 +240,9 @@ class MainWindow(QMainWindow):
print("No path to export.")
return
# We'll consider each anchor point as "USER-PLACED".
# But unlike a distance-threshold approach, we assign each anchor
# to exactly one closest path point.
anchor_points = self.image_view.anchor_points
# For each anchor, find the index of the closest path point
# Finds the index of the closest path point for each anchor point
user_placed_indices = set()
for ax, ay in anchor_points:
min_dist = float('inf')
......@@ -245,7 +277,16 @@ class MainWindow(QMainWindow):
print(f"Exported path with {len(full_xy)} points to {file_path}")
def clear_points(self):
"""
Clears points from the image.
"""
self.image_view.clear_guide_points()
def closeEvent(self, event):
def closeEvent(self, event: QCloseEvent):
"""
Handle the window close event.
Args:
event: The close event.
"""
super().closeEvent(event)
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment