89 lines
3.1 KiB
Python
89 lines
3.1 KiB
Python
from PyQt6.QtWidgets import QLabel, QVBoxLayout, QHBoxLayout, QLineEdit, QCheckBox, QSlider
|
|
from PyQt6.QtGui import QIntValidator
|
|
from PyQt6.QtCore import Qt
|
|
|
|
import numpy as np
|
|
import cv2
|
|
|
|
from .ImageParameterDialog import ImageParameterDialog
|
|
from .. import ImageProcessingWorker
|
|
|
|
class RotationDialog(ImageParameterDialog):
|
|
def __init__(self, image):
|
|
super().__init__(image, ImageProcessingWorker.ImageProcessingWorker)
|
|
self.setWindowTitle("Rotation")
|
|
self.layout = QVBoxLayout()
|
|
|
|
self.angle_label = QLabel("Angle:")
|
|
self.angle_field = QLineEdit()
|
|
self.angle_field.setText("0")
|
|
self.angle_field.setPlaceholderText("Enter rotation angle")
|
|
self.angle_field.setValidator(QIntValidator(-360, 360))
|
|
self.angle_field.textEdited.connect(self.angle_text_changed)
|
|
|
|
self.angle_slider = QSlider(Qt.Orientation.Horizontal)
|
|
self.angle_slider.setRange(-360, 360)
|
|
self.angle_slider.setTickInterval(90)
|
|
self.angle_slider.setTickPosition(QSlider.TickPosition.TicksBelow)
|
|
self.angle_slider.valueChanged.connect(self.angle_slider_changed)
|
|
|
|
self.keep_size_checkbox = QCheckBox("Keep original size")
|
|
self.keep_size_checkbox.setChecked(True)
|
|
self.keep_size_checkbox.stateChanged.connect(self.update)
|
|
|
|
input_layout = QHBoxLayout()
|
|
input_layout.addWidget(self.angle_label)
|
|
input_layout.addWidget(self.angle_field)
|
|
|
|
self.layout.addLayout(input_layout)
|
|
self.layout.addWidget(self.angle_slider)
|
|
self.layout.addWidget(self.keep_size_checkbox)
|
|
self.layout.addWidget(self.button_box)
|
|
|
|
self.setLayout(self.layout)
|
|
|
|
def angle_text_changed(self, text):
|
|
if text:
|
|
angle = int(text)
|
|
self.angle_slider.setValue(angle)
|
|
self.update()
|
|
|
|
def angle_slider_changed(self, value):
|
|
self.angle_field.setText(str(value))
|
|
self.update()
|
|
|
|
def update(self):
|
|
angle = self.angle_field.text()
|
|
if not angle:
|
|
self.set_accept_enable(False)
|
|
return
|
|
self.send_to_process({
|
|
'angle': float(angle),
|
|
'keep_size': self.keep_size_checkbox.isChecked()
|
|
})
|
|
|
|
def process_image(self, image, values):
|
|
angle = values['angle']
|
|
keep_size = values['keep_size']
|
|
|
|
height, width = image.shape[:2]
|
|
center = (width // 2, height // 2)
|
|
matrix = cv2.getRotationMatrix2D(center, angle, 1.0)
|
|
|
|
if keep_size:
|
|
rotated_image = cv2.warpAffine(image, matrix, (width, height))
|
|
else:
|
|
cos = np.abs(matrix[0, 0])
|
|
sin = np.abs(matrix[0, 1])
|
|
new_width = int((height * sin) + (width * cos))
|
|
new_height = int((height * cos) + (width * sin))
|
|
matrix[0, 2] += (new_width / 2) - center[0]
|
|
matrix[1, 2] += (new_height / 2) - center[1]
|
|
rotated_image = cv2.warpAffine(image, matrix, (new_width, new_height))
|
|
self.set_accept_enable(True)
|
|
|
|
return rotated_image
|
|
|
|
@classmethod
|
|
def dialog_name(cls):
|
|
return "Rotate" |