generated from LouisMazin/PythonApplicationTemplate
176 lines
7.0 KiB
Python
176 lines
7.0 KiB
Python
from PyQt6.QtCore import Qt
|
|
from PyQt6.QtGui import QPainter, QPixmap, QPen, QImage
|
|
from PyQt6.QtWidgets import QWidget
|
|
from app.core.main_manager import MainManager
|
|
import numpy as np
|
|
|
|
class ImageViewer(QWidget):
|
|
def __init__(self, parent = None):
|
|
super().__init__(parent)
|
|
self.DicomWindow = parent
|
|
|
|
self.main_manager = MainManager.get_instance()
|
|
self.dicom_manager = self.main_manager.get_dicom_manager()
|
|
|
|
self.display_image = None
|
|
self.original_image = None
|
|
self.drawing = False
|
|
self.brush_size = 10
|
|
self.current_stroke = [] # Points du trait en cours
|
|
self.last_point = None
|
|
self.setMouseTracking(True)
|
|
|
|
def set_image(self, image):
|
|
self.original_image = image
|
|
self.display_image = QPixmap.fromImage(image)
|
|
self.update()
|
|
|
|
def paintEvent(self, event):
|
|
_ = event
|
|
if self.display_image:
|
|
painter = QPainter(self)
|
|
scaled = self.display_image.scaled(self.size(), Qt.AspectRatioMode.KeepAspectRatio, Qt.TransformationMode.FastTransformation)
|
|
x = (self.width() - scaled.width()) // 2
|
|
y = (self.height() - scaled.height()) // 2
|
|
painter.drawPixmap(x, y, scaled)
|
|
|
|
# Dessiner le trait en cours en temps réel avec la même taille que le brush
|
|
if self.drawing and len(self.current_stroke) > 1:
|
|
pen = QPen(Qt.GlobalColor.black, self.brush_size, Qt.PenStyle.SolidLine)
|
|
pen.setCapStyle(Qt.PenCapStyle.RoundCap)
|
|
pen.setJoinStyle(Qt.PenJoinStyle.RoundJoin)
|
|
painter.setPen(pen)
|
|
|
|
# Dessiner des lignes entre les points pour un rendu fluide
|
|
for i in range(1, len(self.current_stroke)):
|
|
painter.drawLine(
|
|
int(self.current_stroke[i-1][0]), int(self.current_stroke[i-1][1]),
|
|
int(self.current_stroke[i][0]), int(self.current_stroke[i][1])
|
|
)
|
|
|
|
def mousePressEvent(self, event):
|
|
if event.buttons() & Qt.MouseButton.LeftButton:
|
|
self.drawing = True
|
|
point = (event.position().x(), event.position().y())
|
|
self.current_stroke = [point]
|
|
self.last_point = point
|
|
|
|
def mouseMoveEvent(self, event):
|
|
if self.drawing:
|
|
current_point = (event.position().x(), event.position().y())
|
|
|
|
# Interpoler avec moins de points pour plus de fluidité
|
|
if self.last_point:
|
|
interpolated_points = self.interpolate_points(self.last_point, current_point, max_distance=3)
|
|
self.current_stroke.extend(interpolated_points)
|
|
else:
|
|
self.current_stroke.append(current_point)
|
|
|
|
self.last_point = current_point
|
|
self.update()
|
|
|
|
def mouseReleaseEvent(self, event):
|
|
if self.drawing:
|
|
self.drawing = False
|
|
mask = self.create_mask_from_stroke()
|
|
if mask is not None:
|
|
self.DicomWindow.apply_brush_mask(mask)
|
|
self.current_stroke = []
|
|
self.last_point = None
|
|
|
|
def wheelEvent(self, event):
|
|
delta = event.angleDelta().y()
|
|
if delta != 0:
|
|
self.DicomWindow.scroll_images(1 if delta > 0 else -1)
|
|
event.accept()
|
|
|
|
def screen_to_image_coords(self, x, y):
|
|
if not self.display_image:
|
|
return None
|
|
|
|
scaled = self.display_image.scaled(self.size(), Qt.AspectRatioMode.KeepAspectRatio, Qt.TransformationMode.FastTransformation)
|
|
x_offset = (self.width() - scaled.width()) // 2
|
|
y_offset = (self.height() - scaled.height()) // 2
|
|
|
|
# Vérifie que le point est dans l'image affichée
|
|
if not (x_offset <= x <= x_offset + scaled.width()) or not (y_offset <= y <= y_offset + scaled.height()):
|
|
return None
|
|
|
|
# Ramène dans les coordonnées de l'image d'origine
|
|
img_x = (x - x_offset) * self.original_image.width() / scaled.width()
|
|
img_y = (y - y_offset) * self.original_image.height() / scaled.height()
|
|
return int(img_x), int(img_y)
|
|
|
|
def interpolate_points(self, start_point, end_point, max_distance=2):
|
|
"""Interpole les points avec une distance maximale réduite pour plus de fluidité"""
|
|
x1, y1 = start_point
|
|
x2, y2 = end_point
|
|
|
|
# Calcule la distance entre les points
|
|
distance = max(abs(x2 - x1), abs(y2 - y1))
|
|
|
|
# Si la distance est petite, pas besoin d'interpoler
|
|
if distance <= max_distance:
|
|
return [end_point]
|
|
|
|
# Crée des points intermédiaires avec une granularité plus fine
|
|
points = []
|
|
steps = max(2, int(distance / max_distance))
|
|
|
|
for i in range(1, steps + 1):
|
|
t = i / steps
|
|
x = x1 + (x2 - x1) * t
|
|
y = y1 + (y2 - y1) * t
|
|
points.append((x, y))
|
|
|
|
return points
|
|
|
|
def create_mask_from_stroke(self):
|
|
"""Crée un mask en utilisant QPainter sur une QImage temporaire"""
|
|
if not self.original_image or len(self.current_stroke) < 1:
|
|
return None
|
|
|
|
# Créer une QImage temporaire de même taille que l'image originale
|
|
mask_image = QImage(self.original_image.width(), self.original_image.height(), QImage.Format.Format_Grayscale8)
|
|
mask_image.fill(0) # Remplir de noir (0)
|
|
|
|
# Calculer les paramètres d'affichage
|
|
scaled = self.display_image.scaled(self.size(), Qt.AspectRatioMode.KeepAspectRatio, Qt.TransformationMode.FastTransformation)
|
|
x_offset = (self.width() - scaled.width()) // 2
|
|
y_offset = (self.height() - scaled.height()) // 2
|
|
|
|
scale_x = self.original_image.width() / scaled.width()
|
|
scale_y = self.original_image.height() / scaled.height()
|
|
|
|
# Dessiner sur le mask avec QPainter
|
|
painter = QPainter(mask_image)
|
|
pen = QPen(Qt.GlobalColor.white, self.brush_size * scale_x, Qt.PenStyle.SolidLine)
|
|
pen.setCapStyle(Qt.PenCapStyle.RoundCap)
|
|
pen.setJoinStyle(Qt.PenJoinStyle.RoundJoin)
|
|
painter.setPen(pen)
|
|
|
|
# Convertir et dessiner les points
|
|
for i in range(1, len(self.current_stroke)):
|
|
# Point précédent
|
|
sx1, sy1 = self.current_stroke[i-1]
|
|
img_x1 = (sx1 - x_offset) * scale_x
|
|
img_y1 = (sy1 - y_offset) * scale_y
|
|
|
|
# Point actuel
|
|
sx2, sy2 = self.current_stroke[i]
|
|
img_x2 = (sx2 - x_offset) * scale_x
|
|
img_y2 = (sy2 - y_offset) * scale_y
|
|
|
|
# Dessiner la ligne
|
|
painter.drawLine(int(img_x1), int(img_y1), int(img_x2), int(img_y2))
|
|
|
|
painter.end()
|
|
|
|
# Convertir en numpy array
|
|
width = mask_image.width()
|
|
height = mask_image.height()
|
|
ptr = mask_image.constBits()
|
|
ptr.setsize(height * width)
|
|
mask_array = np.frombuffer(ptr, np.uint8).reshape((height, width)).copy()
|
|
|
|
return mask_array |