From 10f7b5d83f84133af01f05a87a59b3d1b868e4af Mon Sep 17 00:00:00 2001 From: StableLlama Date: Mon, 10 Feb 2025 21:58:26 +0100 Subject: [PATCH 01/82] Allow zooming on center image --- taggui/widgets/image_viewer.py | 223 ++++++++++++++++++++++++++++++--- taggui/widgets/main_window.py | 57 ++++++++- 2 files changed, 261 insertions(+), 19 deletions(-) diff --git a/taggui/widgets/image_viewer.py b/taggui/widgets/image_viewer.py index 0b67d3cc..b15cc9ab 100644 --- a/taggui/widgets/image_viewer.py +++ b/taggui/widgets/image_viewer.py @@ -1,51 +1,240 @@ from pathlib import Path - -from PySide6.QtCore import QModelIndex, QSize, Qt, Slot -from PySide6.QtGui import QImageReader, QPixmap, QResizeEvent -from PySide6.QtWidgets import QLabel, QSizePolicy, QVBoxLayout, QWidget +from PySide6.QtCore import QModelIndex, QPoint, QPointF, QRect, QSize, Qt, Signal, Slot, QEvent +from PySide6.QtGui import QCursor, QImageReader, QMouseEvent, QPixmap, QResizeEvent, QWheelEvent +from PySide6.QtWidgets import (QFrame, QLabel, QScrollArea, QSizePolicy, QVBoxLayout, + QWidget) from models.proxy_image_list_model import ProxyImageListModel from utils.image import Image - class ImageLabel(QLabel): - def __init__(self): + def __init__(self, scroll_area): super().__init__() + self.scroll_area = scroll_area self.image_path = None + self.is_zoom_to_fit = True + self.zoom_factor = 1.0 + self.in_update = False self.setAlignment(Qt.AlignmentFlag.AlignCenter) self.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding) - # This allows the label to shrink. - self.setMinimumSize(QSize(1, 1)) + self.setScaledContents(False) def resizeEvent(self, event: QResizeEvent): - """Reload the image whenever the label is resized.""" + """Resize the image whenever the label is resized.""" if self.image_path: - self.load_image(self.image_path) + self.update_image() def load_image(self, image_path: Path): self.image_path = image_path - image_reader = QImageReader(str(image_path)) + image_reader = QImageReader(str(self.image_path)) # Rotate the image according to the orientation tag. image_reader.setAutoTransform(True) - pixmap = QPixmap.fromImageReader(image_reader) - pixmap.setDevicePixelRatio(self.devicePixelRatio()) - pixmap = pixmap.scaled( - self.size() * pixmap.devicePixelRatio(), + self.pixmap_orig = QPixmap.fromImageReader(image_reader) + self.pixmap_orig.setDevicePixelRatio(self.devicePixelRatio()) + self.update_image() + + def update_image(self): + if not self.pixmap_orig or self.in_update: + return + + self.in_update = True + + if self.is_zoom_to_fit: + self.zoom_factor = self.zoom_fit_ratio() + + pixmap = self.pixmap_orig.scaled( + self.pixmap_orig.size() * self.pixmap_orig.devicePixelRatio() * self.zoom_factor, Qt.AspectRatioMode.KeepAspectRatio, Qt.TransformationMode.SmoothTransformation) self.setPixmap(pixmap) + self.adjustSize() + self.in_update = False + + def zoom_in(self): + self.is_zoom_to_fit = False # No longer zoom to fit + self.zoom_factor = min(self.zoom_factor * 1.25, 4) + self.update_image() + + def zoom_out(self): + self.is_zoom_to_fit = False # No longer zoom to fit + zoom_fit_ratio = self.zoom_fit_ratio() + self.zoom_factor = max(self.zoom_factor / 1.25, min(self.zoom_fit_ratio(), 1.0)) + if self.zoom_factor == zoom_fit_ratio: + self.is_zoom_to_fit = True # At the limit? Activate fit mode again + self.update_image() + + def zoom_original(self): + self.is_zoom_to_fit = False # No longer zoom to fit + self.zoom_factor = 1.0 + self.update_image() + def zoom_fit(self): + self.is_zoom_to_fit = True + self.update_image() + + def zoom_fit_ratio(self): + widget_width = self.scroll_area.viewport().width() + widget_height = self.scroll_area.viewport().height() + image_width = self.pixmap_orig.width() + image_height = self.pixmap_orig.height() + + if image_width > 0 and image_height > 0: + width_ratio = widget_width / image_width + height_ratio = widget_height / image_height + return min(width_ratio, height_ratio) + + return 1.0 # this should not happen anyway class ImageViewer(QWidget): + zoom = Signal(float, name='zoomChanged') + def __init__(self, proxy_image_list_model: ProxyImageListModel): super().__init__() self.proxy_image_list_model = proxy_image_list_model - self.image_label = ImageLabel() - QVBoxLayout(self).addWidget(self.image_label) + self.drag_start_pos = None + self.drag_image_pos = None + + self.scroll_area = QScrollArea() + self.scroll_area.setFrameStyle(QFrame.NoFrame) + self.scroll_area.setWidgetResizable(True) + self.scroll_area.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded) + self.scroll_area.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded) + # Install event filter on the scroll area as the wheelEvent handler + # didn't catch everything leading to strange bugs during zooming + self.scroll_area.viewport().installEventFilter(self) + layout = QVBoxLayout(self) + layout.addWidget(self.scroll_area) + self.setLayout(layout) + + + self.image_label = ImageLabel(self.scroll_area) + self.scroll_area.setWidget(self.image_label) @Slot() def load_image(self, proxy_image_index: QModelIndex): image: Image = self.proxy_image_list_model.data( proxy_image_index, Qt.ItemDataRole.UserRole) self.image_label.load_image(image.path) + self.zoom_emit() + + @Slot() + def zoom_in(self, center_pos: QPoint = None): + factors = self.get_scroll_area_factors() + self.image_label.zoom_in() + self.move_scroll_area(factors) + self.zoom_emit() + + @Slot() + def zoom_out(self, center_pos: QPoint = None): + factors = self.get_scroll_area_factors() + self.image_label.zoom_out() + self.move_scroll_area(factors) + self.zoom_emit() + + @Slot() + def zoom_original(self): + factors = self.get_scroll_area_factors() + self.image_label.zoom_original() + self.move_scroll_area(factors) + self.zoom_emit() + + @Slot() + def zoom_fit(self): + self.image_label.zoom_fit() + self.zoom_emit() + + def zoom_emit(self): + if self.image_label.is_zoom_to_fit: + self.zoom.emit(-1) + else: + self.zoom.emit(self.image_label.zoom_factor) + + def mousePressEvent(self, event: QMouseEvent): + if event.button() == Qt.MouseButton.MiddleButton: + # Reset zoom - and toggle between original size and fit mode + if self.image_label.is_zoom_to_fit: + self.zoom_original() + else: + self.zoom_fit() + elif event.button() == Qt.MouseButton.LeftButton: + self.drag_start_pos = event.pos() + self.drag_image_pos = (self.scroll_area.horizontalScrollBar().value(), self.scroll_area.verticalScrollBar().value()) + self.setCursor(QCursor(Qt.CursorShape.ClosedHandCursor)) + else: + super().mousePressEvent(event) + + def mouseMoveEvent(self, event: QMouseEvent): + if self.drag_start_pos: + delta = event.pos() - self.drag_start_pos + self.scroll_area.horizontalScrollBar().setValue(self.drag_image_pos[0] - delta.x()) + self.scroll_area.verticalScrollBar().setValue(self.drag_image_pos[1] - delta.y()) + super().mouseMoveEvent(event) + + def mouseReleaseEvent(self, event: QMouseEvent): + if event.button() == Qt.MouseButton.LeftButton: + self.drag_start_pos = None + self.drag_image_pos = None + self.setCursor(QCursor(Qt.CursorShape.ArrowCursor)) + super().mouseReleaseEvent(event) + + def eventFilter(self, source, event): + if event.type() == QEvent.Wheel: + if event.modifiers() & Qt.ControlModifier: + # Handle the control key + mouse wheel event + factors = self.get_scroll_area_factors(event.position()) + + if event.angleDelta().y() > 0: + self.zoom_in() + else: + self.zoom_out() + + self.move_scroll_area(factors) + return True # Event is handled + return super().eventFilter(source, event) + + def get_scroll_area_factors(self, position: QPointF | None = None) -> tuple[float, float, float, float]: + """ + Get the factos (fractions, percentages) of the mouse position on the + image as well as it on the scroll area. + """ + widgetPos = self.image_label.geometry() + image_size = self.image_label.pixmap_orig.size()*self.image_label.zoom_factor + if image_size.width() < self.scroll_area.viewport().width(): + offset_x = (self.scroll_area.width() - image_size.width())/2 + else: + offset_x = 0 + if image_size.height() < self.scroll_area.viewport().height(): + offset_y = (self.scroll_area.height() - image_size.height())/2 + else: + offset_y = 0 + + if position: + img_fac_x = (position.x()-widgetPos.x()-offset_x)/image_size.width() + img_fac_y = (position.y()-widgetPos.y()-offset_y)/image_size.height() + scroll_area_fac_x = position.x() / self.scroll_area.viewport().width() + scroll_area_fac_y = position.y() / self.scroll_area.viewport().height() + else: + # No position -> assume center + img_fac_x = (self.scroll_area.viewport().width()/2-widgetPos.x()-offset_x)/image_size.width() + img_fac_y = (self.scroll_area.viewport().height()/2-widgetPos.y()-offset_y)/image_size.height() + scroll_area_fac_x = 0.5 + scroll_area_fac_y = 0.5 + + return (img_fac_x, img_fac_y, scroll_area_fac_x, scroll_area_fac_y) + + def move_scroll_area(self, factors): + """ + Move the image in the scroll area so that the (fractional) position + on the image appears on the (fractional) position of the scroll area + """ + img_fac_x, img_fac_y, scroll_area_fac_x, scroll_area_fac_y = factors + image_size = self.image_label.pixmap_orig.size()*self.image_label.zoom_factor + if image_size.width() > self.scroll_area.viewport().width(): + viewport_x = scroll_area_fac_x * self.scroll_area.viewport().width() + scroll_area_x = img_fac_x * image_size.width() + self.scroll_area.horizontalScrollBar().setValue(scroll_area_x - viewport_x) + if image_size.height() > self.scroll_area.viewport().height(): + viewport_y = scroll_area_fac_y * self.scroll_area.viewport().height() + scroll_area_y = img_fac_y * image_size.height() + self.scroll_area.verticalScrollBar().setValue(scroll_area_y - viewport_y) diff --git a/taggui/widgets/main_window.py b/taggui/widgets/main_window.py index 78a8cf98..6b780a47 100644 --- a/taggui/widgets/main_window.py +++ b/taggui/widgets/main_window.py @@ -4,8 +4,8 @@ from PySide6.QtGui import (QAction, QCloseEvent, QDesktopServices, QIcon, QKeySequence, QPixmap, QShortcut) from PySide6.QtWidgets import (QApplication, QFileDialog, QMainWindow, - QMessageBox, QStackedWidget, QVBoxLayout, - QWidget) + QMessageBox, QStackedWidget, QToolBar, + QVBoxLayout, QWidget) from transformers import AutoTokenizer from dialogs.batch_reorder_tags_dialog import BatchReorderTagsDialog @@ -62,7 +62,29 @@ def __init__(self, app: QApplication): # everything has the correct font size. self.set_font_size() self.image_viewer = ImageViewer(self.proxy_image_list_model) + self.image_viewer.zoom.connect(self.zoom) self.create_central_widget() + + self.toolbar = QToolBar('Main toolbar', self) + self.toolbar.setObjectName('Main toolbar') + self.toolbar.setFloatable(True) + self.addToolBar(self.toolbar) + self.zoom_fit_best_action = QAction(QIcon.fromTheme('zoom-fit-best'), 'Zoom to fit', self) + self.zoom_fit_best_action.setCheckable(True) + #self.zoom_fit_best_action.triggered.connect(self.image_viewer.zoom_fit) + self.toolbar.addAction(self.zoom_fit_best_action) + self.zoom_in_action = QAction(QIcon.fromTheme('zoom-in'), 'Zoom in', self) + #self.zoom_in_action.triggered.connect(self.image_viewer.zoom_in) + self.toolbar.addAction(self.zoom_in_action) + self.zoom_original_action = QAction(QIcon.fromTheme('zoom-original'), 'Original size', self) + self.zoom_original_action.setCheckable(True) + #self.zoom_original_action.triggered.connect(self.image_viewer.zoom_original) + self.toolbar.addAction(self.zoom_original_action) + self.zoom_out_action = QAction(QIcon.fromTheme('zoom-out'), 'Zoom out', self) + #self.zoom_out_action.triggered.connect(self.image_viewer.zoom_out) + self.toolbar.addAction(self.zoom_out_action) + self.toolbar.addSeparator() + self.image_list = ImageList(self.proxy_image_list_model, tag_separator, image_list_image_width) self.addDockWidget(Qt.DockWidgetArea.LeftDockWidgetArea, @@ -101,6 +123,7 @@ def __init__(self, app: QApplication): self.reload_directory_action.setDisabled(True) self.undo_action = QAction('Undo', parent=self) self.redo_action = QAction('Redo', parent=self) + self.toggle_toolbar_action = QAction('Toolbar', parent=self) self.toggle_image_list_action = QAction('Images', parent=self) self.toggle_image_tags_editor_action = QAction('Image Tags', parent=self) @@ -113,6 +136,7 @@ def __init__(self, app: QApplication): .selectionModel()) self.image_list_model.image_list_selection_model = ( self.image_list_selection_model) + self.connect_toolbar_signals() self.connect_image_list_signals() self.connect_image_tags_editor_signals() self.connect_all_tags_editor_signals() @@ -206,6 +230,18 @@ def create_central_widget(self): central_widget.addWidget(self.image_viewer) self.setCentralWidget(central_widget) + @Slot() + def zoom(self, factor): + if factor < 0: + self.zoom_fit_best_action.setChecked(True) + self.zoom_original_action.setChecked(False) + elif factor == 1.0: + self.zoom_fit_best_action.setChecked(False) + self.zoom_original_action.setChecked(True) + else: + self.zoom_fit_best_action.setChecked(False) + self.zoom_original_action.setChecked(False) + def load_directory(self, path: Path, select_index: int = 0, save_path_to_settings: bool = False): self.directory_path = path.resolve() @@ -355,10 +391,13 @@ def create_menus(self): edit_menu.addAction(remove_empty_tags_action) view_menu = menu_bar.addMenu('View') + self.toggle_toolbar_action.setCheckable(True) self.toggle_image_list_action.setCheckable(True) self.toggle_image_tags_editor_action.setCheckable(True) self.toggle_all_tags_editor_action.setCheckable(True) self.toggle_auto_captioner_action.setCheckable(True) + self.toggle_toolbar_action.triggered.connect( + lambda is_checked: self.toolbar.setVisible(is_checked)) self.toggle_image_list_action.triggered.connect( lambda is_checked: self.image_list.setVisible(is_checked)) self.toggle_image_tags_editor_action.triggered.connect( @@ -367,6 +406,7 @@ def create_menus(self): lambda is_checked: self.all_tags_editor.setVisible(is_checked)) self.toggle_auto_captioner_action.triggered.connect( lambda is_checked: self.auto_captioner.setVisible(is_checked)) + view_menu.addAction(self.toggle_toolbar_action) view_menu.addAction(self.toggle_image_list_action) view_menu.addAction(self.toggle_image_tags_editor_action) view_menu.addAction(self.toggle_all_tags_editor_action) @@ -425,6 +465,19 @@ def save_image_index(self, proxy_image_index: QModelIndex): else 'filtered_image_index') self.settings.setValue(settings_key, proxy_image_index.row()) + def connect_toolbar_signals(self): + self.zoom_fit_best_action.triggered.connect( + self.image_viewer.zoom_fit) + self.zoom_in_action.triggered.connect( + self.image_viewer.zoom_in) + self.zoom_original_action.triggered.connect( + self.image_viewer.zoom_original) + self.zoom_out_action.triggered.connect( + self.image_viewer.zoom_out) + self.toolbar.visibilityChanged.connect( + lambda: self.toggle_toolbar_action.setChecked( + self.toolbar.isVisible())) + def connect_image_list_signals(self): self.image_list.filter_line_edit.textChanged.connect( self.set_image_list_filter) From da3f17c682bd629374a394cc3c949a72b2557b10 Mon Sep 17 00:00:00 2001 From: StableLlama Date: Mon, 3 Feb 2025 19:47:00 +0100 Subject: [PATCH 02/82] Implement export functionality Still missing: - color space handling - JPEG XL (different PR) - crop editor (different PR) --- taggui/dialogs/export_dialog.py | 482 ++++++++++++++++++++++++++++++++ taggui/utils/image.py | 1 + taggui/utils/settings.py | 11 +- taggui/widgets/main_window.py | 10 + 4 files changed, 503 insertions(+), 1 deletion(-) create mode 100644 taggui/dialogs/export_dialog.py diff --git a/taggui/dialogs/export_dialog.py b/taggui/dialogs/export_dialog.py new file mode 100644 index 00000000..55d297aa --- /dev/null +++ b/taggui/dialogs/export_dialog.py @@ -0,0 +1,482 @@ +from enum import Enum +from collections import defaultdict +from math import floor +import os +from pathlib import Path + +from PySide6.QtCore import Qt, Slot +from PySide6.QtWidgets import (QWidget, QDialog, QFileDialog, QGridLayout, QLabel, + QLineEdit, QPushButton, QVBoxLayout, QHBoxLayout, + QTableWidget, QTableWidgetItem, QSizePolicy, + QMessageBox) +from PIL import Image, ImageFilter #, ImageQt, ImageEnhance, ImageCms + +from utils.settings import DEFAULT_SETTINGS, get_settings +from utils.settings_widgets import (SettingsBigCheckBox, SettingsLineEdit, + SettingsSpinBox, SettingsComboBox) +from models.image_list_model import ImageListModel + +Presets = { + 'manual': (0, 0), + 'Direct feed through': (0, 1), + 'SD1': (512, 64), + 'SDXL, SD3, Flux': (1024, 64) +} + +class ExportFormat(str, Enum): + JPG = '.jpg - JPEG' + PNG = '.png - PNG' + WEBP = '.webp - WEBP' + +ExportFormatDict = { + ExportFormat.JPG: 'jpeg', + ExportFormat.PNG: 'png', + ExportFormat.WEBP: 'webp' +} + +class BucketStrategy(str, Enum): + CROP = 'crop' + SCALE = 'scale' + CROP_SCALE = 'crop and scale' + +class ExportDialog(QDialog): + def __init__(self, parent, image_list_model: ImageListModel): + super().__init__(parent) + self.image_list_model = image_list_model + self.settings = get_settings() + self.inhibit_statistics_update = True + self.resolution_cache: dict[tuple, tuple] = {} + self.setWindowTitle('Export') + layout = QVBoxLayout(self) + layout.setContentsMargins(20, 20, 20, 20) + layout.setSpacing(20) + + grid_layout = QGridLayout() + grid_layout.setColumnStretch(0, 0) + grid_layout.setColumnStretch(1, 1) + + grid_row = 0 + grid_layout.addWidget(QLabel('Preset'), grid_row, 0, + Qt.AlignmentFlag.AlignRight) + preset_combo_box = SettingsComboBox(key='export_preset') + preset_combo_box.addItems(list(Presets)) + preset_combo_box.currentTextChanged.connect(self.apply_preset) + grid_layout.addWidget(preset_combo_box, grid_row, 1, + Qt.AlignmentFlag.AlignLeft) + + grid_row += 1 + grid_layout.addWidget(QLabel('Resolution (px)'), grid_row, 0, + Qt.AlignmentFlag.AlignRight) + self.resolution_spin_box = SettingsSpinBox( + key='export_resolution', default=DEFAULT_SETTINGS['export_resolution'], + minimum=0, maximum=8192) + self.resolution_spin_box.setToolTip('Common values:\n' + '0: disable rescaling\n' + '512: SD1.5\n' + '1024: SDXL, SD3, Flux') + self.resolution_spin_box.textChanged.connect(self.show_megapixels) + self.resolution_spin_box.textChanged.connect(self.show_statistics) + grid_layout.addWidget(self.resolution_spin_box, grid_row, 1, + Qt.AlignmentFlag.AlignLeft) + + grid_row += 1 + grid_layout.addWidget(QLabel('Image size (megapixel)'), grid_row, 0, + Qt.AlignmentFlag.AlignRight) + self.megapixels = QLabel('-') + grid_layout.addWidget(self.megapixels, grid_row, 1, + Qt.AlignmentFlag.AlignLeft) + + grid_row += 1 + grid_layout.addWidget(QLabel('Allow upscaling'), grid_row, 0, + Qt.AlignmentFlag.AlignRight) + self.upscaling_check_box = SettingsBigCheckBox( + key='export_upscaling', + default=DEFAULT_SETTINGS['export_upscaling']) + self.upscaling_check_box.setToolTip('Scale too small images to the requested size.\n' + 'This should be avoided as it lowers the image quality.') + self.upscaling_check_box.stateChanged.connect(self.show_statistics) + grid_layout.addWidget(self.upscaling_check_box, grid_row, 1, + Qt.AlignmentFlag.AlignLeft) + + grid_row += 1 + grid_layout.addWidget(QLabel('Bucket resolution size (px)'), grid_row, 0, + Qt.AlignmentFlag.AlignRight) + self.bucket_res_size_spin_box = SettingsSpinBox( + key='export_bucket_res_size', default=DEFAULT_SETTINGS['export_bucket_res_size'], + minimum=1, maximum=256) + self.bucket_res_size_spin_box.setToolTip('Ensure that the exported image size is divisable by that number.\n' + 'It should match the setting on the training tool.\n' + 'It might cause minor cropping.') + self.bucket_res_size_spin_box.textChanged.connect(self.show_statistics) + grid_layout.addWidget(self.bucket_res_size_spin_box, grid_row, 1, + Qt.AlignmentFlag.AlignLeft) + + grid_row += 1 + grid_layout.addWidget(QLabel('Bucket fitting strategy'), grid_row, 0, + Qt.AlignmentFlag.AlignRight) + bucket_strategy_combo_box = SettingsComboBox(key='export_bucket_strategy') + bucket_strategy_combo_box.addItems(list(BucketStrategy)) + bucket_strategy_combo_box.setToolTip('crop: center crop\n' + 'scale: assymetric scaling\n' + 'crop and scale: use both to minimize each effect') + grid_layout.addWidget(bucket_strategy_combo_box, grid_row, 1, + Qt.AlignmentFlag.AlignLeft) + + grid_row += 1 + grid_layout.addWidget(QLabel('Output format'), grid_row, 0, + Qt.AlignmentFlag.AlignRight) + format_widget = QWidget() + format_layout = QHBoxLayout() + self.format_combo_box = SettingsComboBox(key='export_format') + self.format_combo_box.addItems(list(ExportFormat)) + self.format_combo_box.currentTextChanged.connect(self.format_change) + format_layout.addWidget(self.format_combo_box, + Qt.AlignmentFlag.AlignLeft) + format_layout.addWidget(QLabel('Quality'), + Qt.AlignmentFlag.AlignRight) + self.quality_spin_box = SettingsSpinBox( + key='export_quality', default=DEFAULT_SETTINGS['export_quality'], + minimum=0, maximum=100) + self.quality_spin_box.setToolTip('Only for JPEG and WebP.\n' + '0 is worst and 100 is best.\n' + 'For JPEG numbers above 95 should be avoided') + self.quality_spin_box.textChanged.connect(self.quality_change) + format_layout.addWidget(self.quality_spin_box, + Qt.AlignmentFlag.AlignLeft) + format_widget.setLayout(format_layout) + grid_layout.addWidget(format_widget, grid_row, 1, + Qt.AlignmentFlag.AlignLeft) + + grid_row += 1 + grid_layout.addWidget(QLabel('Export directory'), grid_row, 0, + Qt.AlignmentFlag.AlignRight) + self.export_directory_line_edit = SettingsLineEdit( + key='export_directory_path', + default=DEFAULT_SETTINGS['export_directory_path']) + self.export_directory_line_edit.setMinimumWidth(400) + self.export_directory_line_edit.setClearButtonEnabled(True) + grid_layout.addWidget(self.export_directory_line_edit, grid_row, 1, + Qt.AlignmentFlag.AlignLeft) + + grid_row += 1 + export_directory_button = QPushButton('Select Directory...') + export_directory_button.clicked.connect(self.set_export_directory_path) + grid_layout.addWidget(export_directory_button, grid_row, 1, + Qt.AlignmentFlag.AlignLeft) + + grid_row += 1 + grid_layout.addWidget(QLabel('Keep input directory structure'), grid_row, 0, + Qt.AlignmentFlag.AlignRight) + keep_dir_structure_check_box = SettingsBigCheckBox( + key='export_keep_dir_structure', + default=DEFAULT_SETTINGS['export_keep_dir_structure']) + keep_dir_structure_check_box.setToolTip('Keep the subdirectory structure or export\n' + 'all images in the same export directory') + grid_layout.addWidget(keep_dir_structure_check_box, grid_row, 1, + Qt.AlignmentFlag.AlignLeft) + + grid_row += 1 + grid_layout.addWidget(QLabel('Statistics'), grid_row, 0, + Qt.AlignmentFlag.AlignRight) + self.statistics_table = QTableWidget(0, 5, self) + self.statistics_table.setHorizontalHeaderLabels(['Width', 'Height', 'Count', 'Aspect ratio', 'Size utilization']) + self.statistics_table.setMinimumWidth(400) + grid_layout.addWidget(self.statistics_table, grid_row, 1, + Qt.AlignmentFlag.AlignLeft) + + layout.addLayout(grid_layout) + export_button = QPushButton('Export') + export_button.clicked.connect(self.do_export) + layout.addWidget(export_button) + + # update display + self.apply_preset(preset_combo_box.currentText()) + self.show_megapixels() + self.inhibit_statistics_update = False + self.show_statistics() + + @Slot() + def apply_preset(self, value): + if value == 'manual': + self.resolution_spin_box.setEnabled(True) + self.bucket_res_size_spin_box.setEnabled(True) + else: + preset = Presets[value] + self.inhibit_statistics_update = True + self.resolution_spin_box.setValue(preset[0]) + self.resolution_spin_box.setEnabled(False) + self.bucket_res_size_spin_box.setValue(preset[1]) + self.bucket_res_size_spin_box.setEnabled(False) + self.inhibit_statistics_update = False + self.show_statistics() + + @Slot() + def show_megapixels(self): + resolution = self.resolution_spin_box.value() + if resolution > 0: + megapixels = resolution * resolution / 1024 / 1024 + self.megapixels.setText(f"{megapixels:.3f}") + else: + self.megapixels.setText('-') + + @Slot() + def format_change(self, export_format): + if export_format == ExportFormat.JPG: + self.quality_spin_box.setValue(75) + self.quality_spin_box.setEnabled(True) + elif export_format == ExportFormat.PNG: + self.quality_spin_box.setValue(100) + self.quality_spin_box.setEnabled(False) + elif export_format == ExportFormat.WEBP: + self.quality_spin_box.setValue(80) + self.quality_spin_box.setEnabled(True) + + @Slot() + def quality_change(self, quality): + if (self.format_combo_box.currentText() == ExportFormat.JPG) and int(quality) > 95: + self.quality_spin_box.setStyleSheet('background: orange') + else: + self.quality_spin_box.setStyleSheet('') + + @Slot() + def show_statistics(self): + if self.inhibit_statistics_update: + return + + self.resolution_cache = {} + resolution = self.resolution_spin_box.value() + upscaling = self.upscaling_check_box.isChecked() + bucket_res = self.bucket_res_size_spin_box.value() + + # notable aspect ratios + aspect_ratios = [ + (1, 1, 1), + (2, 1, 2/1), + (3, 2, 3/2), + (4, 3, 4/3), + (16, 9, 16/9), + (21, 9, 21/9), + ] + + image_dimensions = defaultdict(int) + for image_index in range(self.image_list_model.rowCount()): + this_image = self.image_list_model.index(image_index).data(Qt.ItemDataRole.UserRole) + this_image.target_dimensions = self.target_dimensions(this_image.dimensions, resolution, upscaling, bucket_res) + image_dimensions[this_image.target_dimensions] += 1 + + sorted_dimensions = sorted( + image_dimensions.items(), + key=lambda x: x[0][0] / x[0][1] # Sort by width/height ratio + ) + + self.statistics_table.setRowCount(0) # clear old data + for dimensions, count in sorted_dimensions: + width, height = dimensions + aspect_ratio = width / height + rowPosition = self.statistics_table.rowCount() + notable_aspect_ratio = '' + for ar in aspect_ratios: + if abs(ar[2] - aspect_ratio) < 1e-3: + notable_aspect_ratio = f" ({ar[0]}:{ar[1]})" + elif abs(1/ar[2] - aspect_ratio) < 1e-3: + notable_aspect_ratio = f" ({ar[1]}:{ar[0]})" + utilization = (width * height)**0.5 / resolution if resolution > 0 else 1 + + self.statistics_table.insertRow(rowPosition) + self.statistics_table.setItem(rowPosition, 0, QTableWidgetItem(str(width))) + self.statistics_table.setItem(rowPosition, 1, QTableWidgetItem(str(height))) + self.statistics_table.setItem(rowPosition, 2, QTableWidgetItem(str(count))) + self.statistics_table.setItem(rowPosition, 3, QTableWidgetItem(f"{aspect_ratio:.3f}{notable_aspect_ratio}")) + self.statistics_table.setItem(rowPosition, 4, QTableWidgetItem(f"{100*utilization:.1f}%")) + + def target_dimensions(self, dimensions, resolution, upscaling, bucket_res): + """ + Given the original width and height, the bucket resolution step size, + and a maximum allowed area, return new dimensions (width, height) + where both dimensions are multiples of `bucket_res`, their product + does not exceed resolution**2, and the new aspect ratio (width/height) + is as close as possible to the original aspect ratio. + + Note: this gives the optimal answer and thus can be slower than the Kohya bucket + algorithm + """ + if resolution == 0: + # no rescale in this case, only cropping + return ((dimensions[0] // bucket_res)*bucket_res, (dimensions[1] // bucket_res)*bucket_res) + + if dimensions in self.resolution_cache: + return self.resolution_cache[dimensions] + + max_area = resolution**2 + + # Compute the original aspect ratio. + target_ratio = dimensions[0] / dimensions[1] + + # The maximum allowed product of multipliers. + T = max_area // (bucket_res * bucket_res) + + best_candidate = None # will hold (new_width, new_height, error, area) + + # Loop over possible values for b (the vertical multiplier). + # We choose b from 1 up to T (although many values will be skipped + # because the corresponding a then makes a * b > T). + for b in range(1, T + 1): + # Choose a so that a / b is as close as possible to target_ratio. + # (We round the ideal value a = target_ratio * b to the nearest integer.) + a = round(target_ratio * b) + if a < 1: + a = 1 # ensure at least bucket_res pixels + + # Check that the candidate image area (in multiplier units) does not exceed T. + if a * b > T: + # If a*b is too big, skip the candidate. + continue + + candidate_width = a * bucket_res + candidate_height = b * bucket_res + candidate_area = candidate_width * candidate_height + + if not upscaling and (candidate_width > dimensions[0] or candidate_height > dimensions[1]): + continue + + # Compute the aspect ratio error. + candidate_ratio = a / b + error = abs(candidate_ratio - target_ratio) + # compute the mean squared error of ratio and normalized maximum size + error = (candidate_ratio - target_ratio)**2 + ((max_area-candidate_area)/max_area)**2 + + # We choose the candidate with the lowest error. In case of a tie, we choose + # the one that uses the largest area (i.e. as close as possible to resolution**2). + if best_candidate is None: + best_candidate = (candidate_width, candidate_height, error, candidate_area) + else: + _, _, best_error, best_area = best_candidate + if (error < best_error) or (abs(error - best_error) < 1e-9 and candidate_area > best_area): + best_candidate = (candidate_width, candidate_height, error, candidate_area) + + # Fallback: if no candidate is found (this shouldn't happen for reasonable values), + # simply return the smallest possible image. + if best_candidate is None: + return bucket_res, bucket_res + else: + new_width, new_height, _, _ = best_candidate + self.resolution_cache[dimensions] = (new_width, new_height) + return new_width, new_height + + @Slot() + def set_export_directory_path(self): + export_directory_path = self.settings.value( + 'export_directory_path', + defaultValue=DEFAULT_SETTINGS['export_directory_path'], type=str) + if export_directory_path: + initial_directory_path = export_directory_path + elif self.settings.contains('directory_path'): + initial_directory_path = self.settings.value('directory_path') + else: + initial_directory_path = '' + export_directory_path = QFileDialog.getExistingDirectory( + parent=self, caption='Select directory for image export', + dir=initial_directory_path) + if export_directory_path: + self.export_directory_line_edit.setText(export_directory_path) + + @Slot() + def do_export(self): + directory_path = self.settings.value('directory_path', type=str) + export_directory_path = Path(self.settings.value('export_directory_path', type=str)) + export_keep_dir_structure = self.settings.value('export_keep_dir_structure', type=bool) + no_overwrite = True + if os.path.exists(export_directory_path): + if os.path.isfile(export_directory_path): + QMessageBox.critical( + self, + 'Path error', + 'The export directory path points to a file and not to a directory' + ) + return + if os.listdir(export_directory_path): + msgBox = QMessageBox() + msgBox.setIcon(QMessageBox.Warning) + msgBox.setWindowTitle('Path warning') + msgBox.setText('The export directory path is not empty') + overwrite_button = msgBox.addButton('Overwrite', QMessageBox.YesRole) + rename_button = msgBox.addButton('Rename', QMessageBox.NoRole) + msgBox.addButton(QMessageBox.Cancel) + msgBox.setDefaultButton(QMessageBox.Cancel) + button = msgBox.exec_() + if button == QMessageBox.Cancel: + return + if msgBox.clickedButton() == overwrite_button: + no_overwrite = False + else: + QMessageBox.critical( + self, + 'Path error', + 'The export directory path does not exist' + ) + return + + resolution = self.resolution_spin_box.value() + upscaling = self.upscaling_check_box.isChecked() + bucket_res = self.bucket_res_size_spin_box.value() + export_format = self.format_combo_box.currentText() + quality = self.quality_spin_box.value() + bucket_strategy = self.settings.value('export_bucket_strategy', type=str) + + for image_index in range(self.image_list_model.rowCount()): + image_entry = self.image_list_model.index(image_index).data(Qt.ItemDataRole.UserRole) + if export_keep_dir_structure: + relative_path = image_entry.path.relative_to(directory_path) + export_path = export_directory_path / relative_path + export_path.parent.mkdir(parents=True, exist_ok=True) + else: + export_path = export_directory_path / image_entry.path.name + export_path = export_path.with_suffix(export_format.split(' ', 1)[0]) + + if no_overwrite: + stem = export_path.stem + counter = 0 + while export_path.exists(): + export_path = export_path.parent / f"{stem}_{counter}{export_path.suffix}" + counter += 1 + + image_file = Image.open(image_entry.path) + # Preserve alpha if present: + if image_file.mode in ("RGBA", "LA", "PA") and not export_format == ExportFormat.JPG: # Check for alpha channels + image_file = image_file.convert("RGBA") + else: + image_file = image_file.convert("RGB") # Otherwise, convert to RGB + + new_width, new_height = image_entry.target_dimensions + current_width, current_height = image_file.size + if bucket_strategy == BucketStrategy.CROP or bucket_strategy == BucketStrategy.CROP_SCALE: + if current_height * new_width / current_width < new_height: # too wide + new_width = floor(current_width * new_height / current_height) + else: # too high + new_height = floor(current_height * new_width / current_width) + if bucket_strategy == BucketStrategy.CROP_SCALE: + new_width = floor((image_entry.target_dimensions[0] + new_width)/2) + new_height = floor((image_entry.target_dimensions[1] + new_height)/2) + if image_file.size[0] != new_width or image_file.size[1] != new_height: + # resize with the best method available + resized_image = image_file.resize((new_width, new_height), Image.LANCZOS) + # followed by a slight sharpening as it should be done + sharpend_image = resized_image.filter(ImageFilter.UnsharpMask(radius = 0.5, percent = 100, threshold = 3)) + else: + sharpend_image = image_file + + # crop to the desired size + current_width, current_height = sharpend_image.size + crop_width = floor((current_width - image_entry.target_dimensions[0]) / 2) + crop_height = floor((current_height - image_entry.target_dimensions[1]) / 2) + cropped_image = sharpend_image.crop((crop_width, crop_height, current_width - crop_width, current_height - crop_height)) + + # TODO: apply color management as the input images might have an + # arbitrary color space that doesn't fit to the expectation + # of the trainer (most likely sRGB, but probably something + # like Rec. 2100 with HDR in the future?) + #final_image = ImageCms.profileToProfile(cropped_image, srgb_profile, profile) + final_image = cropped_image + + final_image.save(export_path, format=ExportFormatDict[export_format], quality=quality, icc_profile=final_image.info.get('icc_profile')) + self.close() diff --git a/taggui/utils/image.py b/taggui/utils/image.py index f2637da4..96554486 100644 --- a/taggui/utils/image.py +++ b/taggui/utils/image.py @@ -8,5 +8,6 @@ class Image: path: Path dimensions: tuple[int, int] | None + target_dimensions: tuple[int, int] | None tags: list[str] = field(default_factory=list) thumbnail: QIcon | None = None diff --git a/taggui/utils/settings.py b/taggui/utils/settings.py index 88e1f0f5..efe94440 100644 --- a/taggui/utils/settings.py +++ b/taggui/utils/settings.py @@ -9,7 +9,16 @@ 'tag_separator': ',', 'insert_space_after_tag_separator': True, 'autocomplete_tags': True, - 'models_directory_path': '' + 'models_directory_path': '', + 'export_preset': 'SDXL, SD3, Flux', + 'export_resolution': 1024, + 'export_upscaling': False, + 'export_bucket_res_size': 64, + 'export_bucket_strategy': 'crop', + 'export_format': '.jpg - JPEG', + 'export_quality': 75, + 'export_directory_path': '', + 'export_keep_dir_structure': False } diff --git a/taggui/widgets/main_window.py b/taggui/widgets/main_window.py index 6b780a47..cdddff32 100644 --- a/taggui/widgets/main_window.py +++ b/taggui/widgets/main_window.py @@ -10,6 +10,7 @@ from dialogs.batch_reorder_tags_dialog import BatchReorderTagsDialog from dialogs.find_and_replace_dialog import FindAndReplaceDialog +from dialogs.export_dialog import ExportDialog from dialogs.settings_dialog import SettingsDialog from models.image_list_model import ImageListModel from models.image_tag_list_model import ImageTagListModel @@ -291,6 +292,12 @@ def reload_directory(self): self.image_list.list_view.setCurrentIndex( self.proxy_image_list_model.index(select_index, 0)) + @Slot() + def export_images_dialog(self): + export_dialog = ExportDialog(parent=self, image_list_model=self.image_list_model) + export_dialog.exec() + return + @Slot() def show_settings_dialog(self): settings_dialog = SettingsDialog(parent=self) @@ -349,6 +356,9 @@ def create_menus(self): [QKeySequence('Ctrl+Shift+L'), QKeySequence('F5')]) self.reload_directory_action.triggered.connect(self.reload_directory) file_menu.addAction(self.reload_directory_action) + export_action = QAction('Export...', parent=self) + export_action.triggered.connect(self.export_images_dialog) + file_menu.addAction(export_action) settings_action = QAction('Settings...', parent=self) settings_action.setShortcut(QKeySequence('Ctrl+Alt+S')) settings_action.triggered.connect(self.show_settings_dialog) From 11b59f5820a8076866f0cd1bb9a70cd2725c6ead Mon Sep 17 00:00:00 2001 From: StableLlama Date: Tue, 4 Feb 2025 21:24:35 +0100 Subject: [PATCH 03/82] Add color space conversion --- taggui/dialogs/export_dialog.py | 57 +++++++++++++++++++++++++++------ taggui/utils/settings.py | 1 + 2 files changed, 49 insertions(+), 9 deletions(-) diff --git a/taggui/dialogs/export_dialog.py b/taggui/dialogs/export_dialog.py index 55d297aa..ff54b327 100644 --- a/taggui/dialogs/export_dialog.py +++ b/taggui/dialogs/export_dialog.py @@ -2,14 +2,16 @@ from collections import defaultdict from math import floor import os +import io from pathlib import Path from PySide6.QtCore import Qt, Slot +from PySide6.QtGui import QColorSpace from PySide6.QtWidgets import (QWidget, QDialog, QFileDialog, QGridLayout, QLabel, QLineEdit, QPushButton, QVBoxLayout, QHBoxLayout, QTableWidget, QTableWidgetItem, QSizePolicy, QMessageBox) -from PIL import Image, ImageFilter #, ImageQt, ImageEnhance, ImageCms +from PIL import Image, ImageFilter, ImageCms from utils.settings import DEFAULT_SETTINGS, get_settings from utils.settings_widgets import (SettingsBigCheckBox, SettingsLineEdit, @@ -34,6 +36,17 @@ class ExportFormat(str, Enum): ExportFormat.WEBP: 'webp' } +class IccProfileList(str, Enum): + SRgb = 'sRGB' + SRgbLinear = 'sRGB (linear gamma)' + AdobeRgb = 'AdobeRGB' + DisplayP3 = 'DisplayP3' + ProPhotoRgb = 'ProPhotoRGB' + # since PySide6.8: + Bt2020 = 'BT.2020' + Bt2100Pq = 'BT.2100(PQ)' + Bt2100Hlg = 'BT.2100 (HLG)' + class BucketStrategy(str, Enum): CROP = 'crop' SCALE = 'scale' @@ -147,6 +160,21 @@ def __init__(self, parent, image_list_model: ImageListModel): grid_layout.addWidget(format_widget, grid_row, 1, Qt.AlignmentFlag.AlignLeft) + grid_row += 1 + grid_layout.addWidget(QLabel('Output color space'), grid_row, 0, + Qt.AlignmentFlag.AlignRight) + color_space_combo_box = SettingsComboBox(key='export_color_space') + color_space_combo_box.addItem("feed through (don't touch)") + color_space_combo_box.addItem('sRGB (implicit, without profile)') + color_space_combo_box.addItems([IccProfileList[e.name] for e in QColorSpace.NamedColorSpace]) + color_space_combo_box.setToolTip('Color space of the exported images.\n' + 'Most likely the trainer expects sRGB!\n' + '\n' + 'Use "feed through" to keep the color space as it is.\n' + 'Use "sRGB (implicit, without profile)" to save in sRGB but don\'t embed the ICC profile to save 8k file size.') + grid_layout.addWidget(color_space_combo_box, grid_row, 1, + Qt.AlignmentFlag.AlignLeft) + grid_row += 1 grid_layout.addWidget(QLabel('Export directory'), grid_row, 0, Qt.AlignmentFlag.AlignRight) @@ -421,6 +449,11 @@ def do_export(self): bucket_res = self.bucket_res_size_spin_box.value() export_format = self.format_combo_box.currentText() quality = self.quality_spin_box.value() + color_space = self.settings.value('export_color_space', type=str) + save_profile = True + if color_space == 'sRGB (implicit, without profile)': + color_space = 'sRGB' + save_profile = False bucket_strategy = self.settings.value('export_bucket_strategy', type=str) for image_index in range(self.image_list_model.rowCount()): @@ -471,12 +504,18 @@ def do_export(self): crop_height = floor((current_height - image_entry.target_dimensions[1]) / 2) cropped_image = sharpend_image.crop((crop_width, crop_height, current_width - crop_width, current_height - crop_height)) - # TODO: apply color management as the input images might have an - # arbitrary color space that doesn't fit to the expectation - # of the trainer (most likely sRGB, but probably something - # like Rec. 2100 with HDR in the future?) - #final_image = ImageCms.profileToProfile(cropped_image, srgb_profile, profile) - final_image = cropped_image - - final_image.save(export_path, format=ExportFormatDict[export_format], quality=quality, icc_profile=final_image.info.get('icc_profile')) + if color_space == "feed through (don't touch)": + cropped_image.save(export_path, format=ExportFormatDict[export_format], quality=quality, icc_profile=cropped_image.info.get('icc_profile')) + else: + source_profile_raw = image_file.info.get('icc_profile') + if source_profile_raw is None: # assume sRGB + source_profile_raw = QColorSpace(QColorSpace.SRgb).iccProfile() + source_profile = ImageCms.ImageCmsProfile(io.BytesIO(source_profile_raw)) + target_profile_raw = QColorSpace(getattr(QColorSpace, IccProfileList(color_space).name)).iccProfile() + target_profile = ImageCms.ImageCmsProfile(io.BytesIO(target_profile_raw)) + final_image = ImageCms.profileToProfile(cropped_image, source_profile, target_profile) + if save_profile: + final_image.save(export_path, format=ExportFormatDict[export_format], quality=quality, icc_profile=target_profile.tobytes()) + else: + final_image.save(export_path, format=ExportFormatDict[export_format], quality=quality, icc_profile=None) self.close() diff --git a/taggui/utils/settings.py b/taggui/utils/settings.py index efe94440..518e8c46 100644 --- a/taggui/utils/settings.py +++ b/taggui/utils/settings.py @@ -17,6 +17,7 @@ 'export_bucket_strategy': 'crop', 'export_format': '.jpg - JPEG', 'export_quality': 75, + 'export_color_space': 'sRGB', 'export_directory_path': '', 'export_keep_dir_structure': False } From 721fe83d4519653cab8a070ae9a130a226b1bc64 Mon Sep 17 00:00:00 2001 From: StableLlama Date: Tue, 4 Feb 2025 23:47:22 +0100 Subject: [PATCH 04/82] Little display fixes and add infrastructure for preferred sizes. --- taggui/dialogs/export_dialog.py | 75 +++++++++++++++++++++------------ taggui/utils/settings.py | 3 +- 2 files changed, 49 insertions(+), 29 deletions(-) diff --git a/taggui/dialogs/export_dialog.py b/taggui/dialogs/export_dialog.py index ff54b327..8a12f6ff 100644 --- a/taggui/dialogs/export_dialog.py +++ b/taggui/dialogs/export_dialog.py @@ -1,8 +1,8 @@ from enum import Enum from collections import defaultdict -from math import floor import os import io +from math import floor from pathlib import Path from PySide6.QtCore import Qt, Slot @@ -19,10 +19,10 @@ from models.image_list_model import ImageListModel Presets = { - 'manual': (0, 0), - 'Direct feed through': (0, 1), - 'SD1': (512, 64), - 'SDXL, SD3, Flux': (1024, 64) + 'manual': (0, 0, '1:1, 2:1, 3:2, 4:3, 16:9, 21:9'), + 'Direct feed through': (0, 1, '1:1, 2:1, 3:2, 4:3, 16:9, 21:9'), + 'SD1': (512, 64, '512:512, 640:320, 576:384, 512:384, 640:384, 704:320'), + 'SDXL, SD3, Flux': (1024, 64, '1024:1024, 1408:704, 1216:832, 1152:896, 1344:768, 1536:640') } class ExportFormat(str, Enum): @@ -99,18 +99,6 @@ def __init__(self, parent, image_list_model: ImageListModel): grid_layout.addWidget(self.megapixels, grid_row, 1, Qt.AlignmentFlag.AlignLeft) - grid_row += 1 - grid_layout.addWidget(QLabel('Allow upscaling'), grid_row, 0, - Qt.AlignmentFlag.AlignRight) - self.upscaling_check_box = SettingsBigCheckBox( - key='export_upscaling', - default=DEFAULT_SETTINGS['export_upscaling']) - self.upscaling_check_box.setToolTip('Scale too small images to the requested size.\n' - 'This should be avoided as it lowers the image quality.') - self.upscaling_check_box.stateChanged.connect(self.show_statistics) - grid_layout.addWidget(self.upscaling_check_box, grid_row, 1, - Qt.AlignmentFlag.AlignLeft) - grid_row += 1 grid_layout.addWidget(QLabel('Bucket resolution size (px)'), grid_row, 0, Qt.AlignmentFlag.AlignRight) @@ -124,6 +112,30 @@ def __init__(self, parent, image_list_model: ImageListModel): grid_layout.addWidget(self.bucket_res_size_spin_box, grid_row, 1, Qt.AlignmentFlag.AlignLeft) + grid_row += 1 + grid_layout.addWidget(QLabel('Prefered sizes'), grid_row, 0, + Qt.AlignmentFlag.AlignRight) + self.preferred_sizes_line_edit = SettingsLineEdit( + key='export_preferred_sizes', + default=DEFAULT_SETTINGS['export_preferred_sizes']) + self.preferred_sizes_line_edit.setMinimumWidth(500) + self.preferred_sizes_line_edit.setToolTip('Comma separated list of preferred sizes and aspect ratios.\n' + "The inverse aspect ratio is automatically derived and doesn't need to be included.") + grid_layout.addWidget(self.preferred_sizes_line_edit, grid_row, 1, + Qt.AlignmentFlag.AlignLeft) + + grid_row += 1 + grid_layout.addWidget(QLabel('Allow upscaling'), grid_row, 0, + Qt.AlignmentFlag.AlignRight) + self.upscaling_check_box = SettingsBigCheckBox( + key='export_upscaling', + default=DEFAULT_SETTINGS['export_upscaling']) + self.upscaling_check_box.setToolTip('Scale too small images to the requested size.\n' + 'This should be avoided as it lowers the image quality.') + self.upscaling_check_box.stateChanged.connect(self.show_statistics) + grid_layout.addWidget(self.upscaling_check_box, grid_row, 1, + Qt.AlignmentFlag.AlignLeft) + grid_row += 1 grid_layout.addWidget(QLabel('Bucket fitting strategy'), grid_row, 0, Qt.AlignmentFlag.AlignRight) @@ -140,6 +152,9 @@ def __init__(self, parent, image_list_model: ImageListModel): Qt.AlignmentFlag.AlignRight) format_widget = QWidget() format_layout = QHBoxLayout() + format_layout.setContentsMargins(0, 0, 0, 0) + current_format = self.settings.value('export_format', type=str) + current_quality = self.settings.value('export_quality', type=str) self.format_combo_box = SettingsComboBox(key='export_format') self.format_combo_box.addItems(list(ExportFormat)) self.format_combo_box.currentTextChanged.connect(self.format_change) @@ -159,6 +174,9 @@ def __init__(self, parent, image_list_model: ImageListModel): format_widget.setLayout(format_layout) grid_layout.addWidget(format_widget, grid_row, 1, Qt.AlignmentFlag.AlignLeft) + # ensure correct enable/disable and background color of the quality + self.format_change(current_format, False) + self.quality_change(current_quality) grid_row += 1 grid_layout.addWidget(QLabel('Output color space'), grid_row, 0, @@ -181,7 +199,7 @@ def __init__(self, parent, image_list_model: ImageListModel): self.export_directory_line_edit = SettingsLineEdit( key='export_directory_path', default=DEFAULT_SETTINGS['export_directory_path']) - self.export_directory_line_edit.setMinimumWidth(400) + self.export_directory_line_edit.setMinimumWidth(500) self.export_directory_line_edit.setClearButtonEnabled(True) grid_layout.addWidget(self.export_directory_line_edit, grid_row, 1, Qt.AlignmentFlag.AlignLeft) @@ -208,7 +226,7 @@ def __init__(self, parent, image_list_model: ImageListModel): Qt.AlignmentFlag.AlignRight) self.statistics_table = QTableWidget(0, 5, self) self.statistics_table.setHorizontalHeaderLabels(['Width', 'Height', 'Count', 'Aspect ratio', 'Size utilization']) - self.statistics_table.setMinimumWidth(400) + self.statistics_table.setMinimumWidth(500) grid_layout.addWidget(self.statistics_table, grid_row, 1, Qt.AlignmentFlag.AlignLeft) @@ -218,25 +236,26 @@ def __init__(self, parent, image_list_model: ImageListModel): layout.addWidget(export_button) # update display - self.apply_preset(preset_combo_box.currentText()) + self.apply_preset(preset_combo_box.currentText(), False) self.show_megapixels() self.inhibit_statistics_update = False self.show_statistics() @Slot() - def apply_preset(self, value): + def apply_preset(self, value, do_value_change = True): + preset = Presets[value] if value == 'manual': self.resolution_spin_box.setEnabled(True) self.bucket_res_size_spin_box.setEnabled(True) else: - preset = Presets[value] self.inhibit_statistics_update = True - self.resolution_spin_box.setValue(preset[0]) + self.resolution_spin_box.setValue(preset[0]) if do_value_change else 0 self.resolution_spin_box.setEnabled(False) - self.bucket_res_size_spin_box.setValue(preset[1]) + self.bucket_res_size_spin_box.setValue(preset[1]) if do_value_change else 0 self.bucket_res_size_spin_box.setEnabled(False) self.inhibit_statistics_update = False self.show_statistics() + self.preferred_sizes_line_edit.setText(preset[2]) if do_value_change else 0 @Slot() def show_megapixels(self): @@ -248,15 +267,15 @@ def show_megapixels(self): self.megapixels.setText('-') @Slot() - def format_change(self, export_format): + def format_change(self, export_format, do_value_change = True): if export_format == ExportFormat.JPG: - self.quality_spin_box.setValue(75) + self.quality_spin_box.setValue(75) if do_value_change else 0 self.quality_spin_box.setEnabled(True) elif export_format == ExportFormat.PNG: - self.quality_spin_box.setValue(100) + self.quality_spin_box.setValue(100) if do_value_change else 0 self.quality_spin_box.setEnabled(False) elif export_format == ExportFormat.WEBP: - self.quality_spin_box.setValue(80) + self.quality_spin_box.setValue(80) if do_value_change else 0 self.quality_spin_box.setEnabled(True) @Slot() diff --git a/taggui/utils/settings.py b/taggui/utils/settings.py index 518e8c46..d256d2c4 100644 --- a/taggui/utils/settings.py +++ b/taggui/utils/settings.py @@ -12,8 +12,9 @@ 'models_directory_path': '', 'export_preset': 'SDXL, SD3, Flux', 'export_resolution': 1024, - 'export_upscaling': False, 'export_bucket_res_size': 64, + 'export_preferred_sizes' : '1024:1024, 1408:704, 1216:832, 1152:896, 1344:768, 1536:640', + 'export_upscaling': False, 'export_bucket_strategy': 'crop', 'export_format': '.jpg - JPEG', 'export_quality': 75, From 40e45ff1333d9414f787c0b146c36b7556b08a6f Mon Sep 17 00:00:00 2001 From: StableLlama Date: Wed, 5 Feb 2025 22:44:05 +0100 Subject: [PATCH 05/82] Change algorithm for bucketing --- taggui/dialogs/export_dialog.py | 149 +++++++++++++++++++------------- 1 file changed, 90 insertions(+), 59 deletions(-) diff --git a/taggui/dialogs/export_dialog.py b/taggui/dialogs/export_dialog.py index 8a12f6ff..63ee2e41 100644 --- a/taggui/dialogs/export_dialog.py +++ b/taggui/dialogs/export_dialog.py @@ -2,7 +2,8 @@ from collections import defaultdict import os import io -from math import floor +import re +from math import floor, sqrt from pathlib import Path from PySide6.QtCore import Qt, Slot @@ -21,7 +22,7 @@ Presets = { 'manual': (0, 0, '1:1, 2:1, 3:2, 4:3, 16:9, 21:9'), 'Direct feed through': (0, 1, '1:1, 2:1, 3:2, 4:3, 16:9, 21:9'), - 'SD1': (512, 64, '512:512, 640:320, 576:384, 512:384, 640:384, 704:320'), + 'SD1': (512, 64, '512:512, 640:320, 576:384, 512:384, 640:384, 768:320'), 'SDXL, SD3, Flux': (1024, 64, '1024:1024, 1408:704, 1216:832, 1152:896, 1344:768, 1536:640') } @@ -113,7 +114,7 @@ def __init__(self, parent, image_list_model: ImageListModel): Qt.AlignmentFlag.AlignLeft) grid_row += 1 - grid_layout.addWidget(QLabel('Prefered sizes'), grid_row, 0, + grid_layout.addWidget(QLabel('Preferred sizes'), grid_row, 0, Qt.AlignmentFlag.AlignRight) self.preferred_sizes_line_edit = SettingsLineEdit( key='export_preferred_sizes', @@ -304,6 +305,25 @@ def show_statistics(self): (16, 9, 16/9), (21, 9, 21/9), ] + self.preferred_sizes = [] + for res_str in re.split(r'\s*,\s*', self.settings.value('export_preferred_sizes')): + try: + size_str = res_str.split(':') + width = max(int(size_str[0]), int(size_str[1])) + height = min(int(size_str[0]), int(size_str[1])) + self.preferred_sizes.append((width, height)) + if not width == height: + self.preferred_sizes.append((height, width)) + # add exact aspect ratio of the preferred size to label it similar to the perfect one + aspect_ratio = width / height + for ar in aspect_ratios: + if abs(ar[2] - aspect_ratio) < 0.15: + aspect_ratios.append((ar[0], ar[1], aspect_ratio)) + break + except ValueError: + # Handle cases where the resolution string is not in the correct format + print(f"Warning: Invalid resolution format: {res_str}. Skipping.") + continue # Skip to the next resolution if there's an error image_dimensions = defaultdict(int) for image_index in range(self.image_list_model.rowCount()): @@ -347,68 +367,79 @@ def target_dimensions(self, dimensions, resolution, upscaling, bucket_res): Note: this gives the optimal answer and thus can be slower than the Kohya bucket algorithm """ + width, height = dimensions if resolution == 0: # no rescale in this case, only cropping - return ((dimensions[0] // bucket_res)*bucket_res, (dimensions[1] // bucket_res)*bucket_res) + return ((width // bucket_res)*bucket_res, (height // bucket_res)*bucket_res) + + if width < bucket_res or height < bucket_res: + # it doesn't make sense to use such a small image. But we shouldn't + # patronize the user + return dimensions if dimensions in self.resolution_cache: return self.resolution_cache[dimensions] - max_area = resolution**2 - - # Compute the original aspect ratio. - target_ratio = dimensions[0] / dimensions[1] - - # The maximum allowed product of multipliers. - T = max_area // (bucket_res * bucket_res) - - best_candidate = None # will hold (new_width, new_height, error, area) - - # Loop over possible values for b (the vertical multiplier). - # We choose b from 1 up to T (although many values will be skipped - # because the corresponding a then makes a * b > T). - for b in range(1, T + 1): - # Choose a so that a / b is as close as possible to target_ratio. - # (We round the ideal value a = target_ratio * b to the nearest integer.) - a = round(target_ratio * b) - if a < 1: - a = 1 # ensure at least bucket_res pixels - - # Check that the candidate image area (in multiplier units) does not exceed T. - if a * b > T: - # If a*b is too big, skip the candidate. - continue - - candidate_width = a * bucket_res - candidate_height = b * bucket_res - candidate_area = candidate_width * candidate_height - - if not upscaling and (candidate_width > dimensions[0] or candidate_height > dimensions[1]): - continue - - # Compute the aspect ratio error. - candidate_ratio = a / b - error = abs(candidate_ratio - target_ratio) - # compute the mean squared error of ratio and normalized maximum size - error = (candidate_ratio - target_ratio)**2 + ((max_area-candidate_area)/max_area)**2 - - # We choose the candidate with the lowest error. In case of a tie, we choose - # the one that uses the largest area (i.e. as close as possible to resolution**2). - if best_candidate is None: - best_candidate = (candidate_width, candidate_height, error, candidate_area) - else: - _, _, best_error, best_area = best_candidate - if (error < best_error) or (abs(error - best_error) < 1e-9 and candidate_area > best_area): - best_candidate = (candidate_width, candidate_height, error, candidate_area) - - # Fallback: if no candidate is found (this shouldn't happen for reasonable values), - # simply return the smallest possible image. - if best_candidate is None: - return bucket_res, bucket_res - else: - new_width, new_height, _, _ = best_candidate - self.resolution_cache[dimensions] = (new_width, new_height) - return new_width, new_height + preferred_sizes_bonus = 0.4 # reduce the loss by this factor + + max_pixels = resolution * resolution + opt_width = resolution * sqrt(width/height) + opt_height = resolution * sqrt(height/width) + if not upscaling: + opt_width = min(width, opt_width) + opt_height = min(height, opt_height) + + # test 1, guaranteed to find a solution: shrink and crop + # 1.1: exact width + candidate_width = (opt_width // bucket_res) * bucket_res + candidate_height = ((height * candidate_width / width) // bucket_res) * bucket_res + loss = ((height * candidate_width / width) - candidate_height) * candidate_width + if (candidate_width, candidate_height) in self.preferred_sizes: + loss *= preferred_sizes_bonus + # 1.2: exact height + test_height = (opt_height // bucket_res) * bucket_res + test_width = ((width * test_height / height) // bucket_res) * bucket_res + test_loss = ((width * test_height / height) - test_width) * test_height + if (test_height, test_width) in self.preferred_sizes: + test_loss *= preferred_sizes_bonus + if test_loss < loss: + candidate_width = test_width + candidate_height = test_height + loss = test_loss + + # test 2, going bigger might still fit in the size budget due to cropping + # 2.1: exact width + for delta in range(1, 10): + test_width = (opt_width // bucket_res + delta) * bucket_res + test_height = ((height * test_width / width) // bucket_res) * bucket_res + if test_width * test_height > max_pixels: + break + if (test_width > width or test_height > height) and not upscaling: + break + test_loss = ((height * test_width / width) - test_height) * test_width + if (test_height, test_width) in self.preferred_sizes: + test_loss *= preferred_sizes_bonus + if test_loss < loss: + candidate_width = test_width + candidate_height = test_height + loss = test_loss + # 2.2: exact height + for delta in range(1, 10): + test_height = (opt_height // bucket_res + delta) * bucket_res + test_width = ((width * test_height / height) // bucket_res) * bucket_res + if test_width * test_height > max_pixels: + break + if (test_width > width or test_height > height) and not upscaling: + break + test_loss = ((width * test_height / height) - test_width) * test_height + if (test_height, test_width) in self.preferred_sizes: + test_loss *= preferred_sizes_bonus + if test_loss < loss: + candidate_width = test_width + candidate_height = test_height + loss = test_loss + + return int(candidate_width), int(candidate_height) @Slot() def set_export_directory_path(self): From 165a659dbcdb39a0be13135b2620a8e677306e3c Mon Sep 17 00:00:00 2001 From: StableLlama Date: Wed, 5 Feb 2025 23:18:33 +0100 Subject: [PATCH 06/82] Code documentation --- taggui/dialogs/export_dialog.py | 58 ++++++++++++++++++++++++++------- 1 file changed, 46 insertions(+), 12 deletions(-) diff --git a/taggui/dialogs/export_dialog.py b/taggui/dialogs/export_dialog.py index 63ee2e41..42e793f8 100644 --- a/taggui/dialogs/export_dialog.py +++ b/taggui/dialogs/export_dialog.py @@ -55,6 +55,9 @@ class BucketStrategy(str, Enum): class ExportDialog(QDialog): def __init__(self, parent, image_list_model: ImageListModel): + """ + Main method to create the export dialog. + """ super().__init__(parent) self.image_list_model = image_list_model self.settings = get_settings() @@ -243,7 +246,11 @@ def __init__(self, parent, image_list_model: ImageListModel): self.show_statistics() @Slot() - def apply_preset(self, value, do_value_change = True): + def apply_preset(self, value: str, do_value_change: bool = True): + """ + Slot to call when a new preset was selected to help the user to set + important settings to a consistent state. + """ preset = Presets[value] if value == 'manual': self.resolution_spin_box.setEnabled(True) @@ -260,6 +267,10 @@ def apply_preset(self, value, do_value_change = True): @Slot() def show_megapixels(self): + """ + Slot to call when the resolution was changes to update the megapixel + display. + """ resolution = self.resolution_spin_box.value() if resolution > 0: megapixels = resolution * resolution / 1024 / 1024 @@ -268,7 +279,10 @@ def show_megapixels(self): self.megapixels.setText('-') @Slot() - def format_change(self, export_format, do_value_change = True): + def format_change(self, export_format: ExportFormat, do_value_change: bool = True): + """ + Slot to call when the export format was changed. + """ if export_format == ExportFormat.JPG: self.quality_spin_box.setValue(75) if do_value_change else 0 self.quality_spin_box.setEnabled(True) @@ -280,7 +294,10 @@ def format_change(self, export_format, do_value_change = True): self.quality_spin_box.setEnabled(True) @Slot() - def quality_change(self, quality): + def quality_change(self, quality: str): + """ + Slot to call when the export quality setting was changed. + """ if (self.format_combo_box.currentText() == ExportFormat.JPG) and int(quality) > 95: self.quality_spin_box.setStyleSheet('background: orange') else: @@ -288,6 +305,9 @@ def quality_change(self, quality): @Slot() def show_statistics(self): + """ + Update the statistics table content. + """ if self.inhibit_statistics_update: return @@ -356,16 +376,24 @@ def show_statistics(self): self.statistics_table.setItem(rowPosition, 3, QTableWidgetItem(f"{aspect_ratio:.3f}{notable_aspect_ratio}")) self.statistics_table.setItem(rowPosition, 4, QTableWidgetItem(f"{100*utilization:.1f}%")) - def target_dimensions(self, dimensions, resolution, upscaling, bucket_res): + def target_dimensions(self, dimensions: tuple[int, int], resolution: int, upscaling: bool, bucket_res: int): """ - Given the original width and height, the bucket resolution step size, - and a maximum allowed area, return new dimensions (width, height) - where both dimensions are multiples of `bucket_res`, their product - does not exceed resolution**2, and the new aspect ratio (width/height) - is as close as possible to the original aspect ratio. - - Note: this gives the optimal answer and thus can be slower than the Kohya bucket - algorithm + Determine the dimensions of an image it should have when it is exported. + + Note: this gives the optimal answer and thus can be slower than the Kohya + bucket algorithm. + + Parameters + ---------- + dimensions : tuple[int, int] + The width and height of the image + resolution : int + The target resolution of the AI model. The target image pixels + will not exceed the square of this number + upscaling : bool + Is upscaling of images allowed? + bucket_res : int + The resolution of the buckets """ width, height = dimensions if resolution == 0: @@ -443,6 +471,9 @@ def target_dimensions(self, dimensions, resolution, upscaling, bucket_res): @Slot() def set_export_directory_path(self): + """ + Set the path of the directory to export to. + """ export_directory_path = self.settings.value( 'export_directory_path', defaultValue=DEFAULT_SETTINGS['export_directory_path'], type=str) @@ -460,6 +491,9 @@ def set_export_directory_path(self): @Slot() def do_export(self): + """ + Export all images with the configured settings. + """ directory_path = self.settings.value('directory_path', type=str) export_directory_path = Path(self.settings.value('export_directory_path', type=str)) export_keep_dir_structure = self.settings.value('export_keep_dir_structure', type=bool) From f50b34025b202036559c69d7a82cdcc017ede82a Mon Sep 17 00:00:00 2001 From: StableLlama Date: Wed, 5 Feb 2025 23:39:10 +0100 Subject: [PATCH 07/82] Add documentation --- README.md | 55 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/README.md b/README.md index c589a5be..961f6bdb 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,7 @@ like Stable Diffusion. Tagger, and many more - Batch tag operations for renaming, deleting, and sorting tags - Advanced image list filtering +- Export images ready to be used for training ## Installation @@ -253,3 +254,57 @@ You can nest parentheses and operators to create arbitrarily complex filters. The `Edit` menu contains additional features for batch tag operations, such as `Find and Replace` (`Ctrl`+`R`) and `Batch Reorder Tags` (`Ctrl`+`B`). + +## Export + +Exporting the images to a directory allows different options. By choosing the +preset for the target AI model many important settings are automatically set. + +Resolution +: The native resolution of the model, like 1024 for SDXL or Flux. + +Image size +: A hint showing the megapixels. The exported images will not exceed this +: number. + +Bucket resolution size +: The bucket size the training tool is using. + +Preferres sizes +: A comma separated list of target sizes that should be preferred for the +: exported images. + +Allow upscaling +: Do upscale images when set. This is bad for the quality but might reduce the +: number of buckets that must be used for training. + +Bucket fitting strategy +: The method to make sure an image fits into a bucket. It can be a direct crop +: that removes information from the side of an image. Or a scaling that changes +: the aspect ratio of an image and can create slight distortions. Or a +: combination of both that reduces each effect. + +Output format +: The file type and quality setting for formats that have a lossy compression. +: Note: for JPEG a number above 95 should be avoided. + +Output color space +: Most models will expect the images in sRGB format and don't contain any +: color management. So it is important that the exporter handles this as +: the images used for the training might use a different color space. +: To save 8 kB for each image you might want to select "sRGB implicit" as that +: converts the image to sRGB but doesn't store the ICC information. +: When no color space convertation should happen you can choose "feed through". +: +: The simple "sRGB" is most likely the setting you want to choose here unless +: you are an expert and have special requirements. + +Export directory +: The place to export the images to. + +Keep input directory structure +: When the source images are organized in subdirectories this structure will +: be used for the exported images as well when selected. + +Statistics +: Preview of the generated image sizes from the export funtion. From ecbf8b8069fe12d8dc414c93ad67125bf466d8bc Mon Sep 17 00:00:00 2001 From: StableLlama Date: Wed, 5 Feb 2025 23:43:15 +0100 Subject: [PATCH 08/82] Fix markdown style --- README.md | 96 +++++++++++++++++++++++++++---------------------------- 1 file changed, 48 insertions(+), 48 deletions(-) diff --git a/README.md b/README.md index 961f6bdb..ec25985b 100644 --- a/README.md +++ b/README.md @@ -260,51 +260,51 @@ The `Edit` menu contains additional features for batch tag operations, such as Exporting the images to a directory allows different options. By choosing the preset for the target AI model many important settings are automatically set. -Resolution -: The native resolution of the model, like 1024 for SDXL or Flux. - -Image size -: A hint showing the megapixels. The exported images will not exceed this -: number. - -Bucket resolution size -: The bucket size the training tool is using. - -Preferres sizes -: A comma separated list of target sizes that should be preferred for the -: exported images. - -Allow upscaling -: Do upscale images when set. This is bad for the quality but might reduce the -: number of buckets that must be used for training. - -Bucket fitting strategy -: The method to make sure an image fits into a bucket. It can be a direct crop -: that removes information from the side of an image. Or a scaling that changes -: the aspect ratio of an image and can create slight distortions. Or a -: combination of both that reduces each effect. - -Output format -: The file type and quality setting for formats that have a lossy compression. -: Note: for JPEG a number above 95 should be avoided. - -Output color space -: Most models will expect the images in sRGB format and don't contain any -: color management. So it is important that the exporter handles this as -: the images used for the training might use a different color space. -: To save 8 kB for each image you might want to select "sRGB implicit" as that -: converts the image to sRGB but doesn't store the ICC information. -: When no color space convertation should happen you can choose "feed through". -: -: The simple "sRGB" is most likely the setting you want to choose here unless -: you are an expert and have special requirements. - -Export directory -: The place to export the images to. - -Keep input directory structure -: When the source images are organized in subdirectories this structure will -: be used for the exported images as well when selected. - -Statistics -: Preview of the generated image sizes from the export funtion. +`Resolution`: +The native resolution of the model, like 1024 for SDXL or Flux. + +`Image size`: +A hint showing the megapixels. The exported images will not exceed this +number. + +`Bucket resolution size`: +The bucket size the training tool is using. + +`Preferres sizes`: +A comma separated list of target sizes that should be preferred for the +exported images. + +`Allow upscaling`: +Do upscale images when set. This is bad for the quality but might reduce the +number of buckets that must be used for training. + +`Bucket fitting strategy`: +The method to make sure an image fits into a bucket. It can be a direct crop +that removes information from the side of an image. Or a scaling that changes +the aspect ratio of an image and can create slight distortions. Or a +combination of both that reduces each effect. + +`Output format`: +The file type and quality setting for formats that have a lossy compression. +Note: for JPEG a number above 95 should be avoided. + +`Output color space`: +Most models will expect the images in sRGB format and don't contain any +color management. So it is important that the exporter handles this as +the images used for the training might use a different color space. +To save 8 kB for each image you might want to select "sRGB implicit" as that +converts the image to sRGB but doesn't store the ICC information. +When no color space convertation should happen you can choose "feed through". + +The simple "sRGB" is most likely the setting you want to choose here unless +you are an expert and have special requirements. + +`Export directory`: +The place to export the images to. + +`Keep input directory structure`: +When the source images are organized in subdirectories this structure will +be used for the exported images as well when selected. + +`Statistics`: +Preview of the generated image sizes from the export funtion. From 884206b184ee41d8bb4b970218ddcd0434e78701 Mon Sep 17 00:00:00 2001 From: StableLlama Date: Thu, 6 Feb 2025 00:18:54 +0100 Subject: [PATCH 09/82] Make sure to export the caption files as well --- taggui/dialogs/export_dialog.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/taggui/dialogs/export_dialog.py b/taggui/dialogs/export_dialog.py index 42e793f8..515c9f69 100644 --- a/taggui/dialogs/export_dialog.py +++ b/taggui/dialogs/export_dialog.py @@ -5,6 +5,7 @@ import re from math import floor, sqrt from pathlib import Path +import shutil from PySide6.QtCore import Qt, Slot from PySide6.QtGui import QColorSpace @@ -557,6 +558,11 @@ def do_export(self): export_path = export_path.parent / f"{stem}_{counter}{export_path.suffix}" counter += 1 + # copy the tag file first + if image_entry.path.with_suffix('.txt').exists(): + shutil.copyfile(str(image_entry.path.with_suffix('.txt')), str(export_path.with_suffix('.txt'))) + + # then handle the image image_file = Image.open(image_entry.path) # Preserve alpha if present: if image_file.mode in ("RGBA", "LA", "PA") and not export_format == ExportFormat.JPG: # Check for alpha channels From 3b7be6110923d258bded6203601bab6dc8cc6737 Mon Sep 17 00:00:00 2001 From: StableLlama Date: Thu, 6 Feb 2025 23:51:54 +0100 Subject: [PATCH 10/82] Refactor DEFAULT_SETTINGS to ease initial value access Code refactor to respect mostly a width of 80 --- taggui/dialogs/export_dialog.py | 135 +++++++++++++++++------------- taggui/dialogs/settings_dialog.py | 12 +-- taggui/utils/settings_widgets.py | 33 +++++--- 3 files changed, 103 insertions(+), 77 deletions(-) diff --git a/taggui/dialogs/export_dialog.py b/taggui/dialogs/export_dialog.py index 515c9f69..b3997bb3 100644 --- a/taggui/dialogs/export_dialog.py +++ b/taggui/dialogs/export_dialog.py @@ -9,10 +9,10 @@ from PySide6.QtCore import Qt, Slot from PySide6.QtGui import QColorSpace -from PySide6.QtWidgets import (QWidget, QDialog, QFileDialog, QGridLayout, QLabel, - QLineEdit, QPushButton, QVBoxLayout, QHBoxLayout, - QTableWidget, QTableWidgetItem, QSizePolicy, - QMessageBox) +from PySide6.QtWidgets import (QWidget, QDialog, QFileDialog, QGridLayout, + QLabel, QLineEdit, QPushButton, QTableWidget, + QTableWidgetItem, QProgressBar, QMessageBox, + QVBoxLayout, QHBoxLayout, QSizePolicy) from PIL import Image, ImageFilter, ImageCms from utils.settings import DEFAULT_SETTINGS, get_settings @@ -44,7 +44,6 @@ class IccProfileList(str, Enum): AdobeRgb = 'AdobeRGB' DisplayP3 = 'DisplayP3' ProPhotoRgb = 'ProPhotoRGB' - # since PySide6.8: Bt2020 = 'BT.2020' Bt2100Pq = 'BT.2100(PQ)' Bt2100Hlg = 'BT.2100 (HLG)' @@ -65,9 +64,9 @@ def __init__(self, parent, image_list_model: ImageListModel): self.inhibit_statistics_update = True self.resolution_cache: dict[tuple, tuple] = {} self.setWindowTitle('Export') - layout = QVBoxLayout(self) - layout.setContentsMargins(20, 20, 20, 20) - layout.setSpacing(20) + self.layout = QVBoxLayout(self) + self.layout.setContentsMargins(20, 20, 20, 20) + self.layout.setSpacing(20) grid_layout = QGridLayout() grid_layout.setColumnStretch(0, 0) @@ -86,12 +85,13 @@ def __init__(self, parent, image_list_model: ImageListModel): grid_layout.addWidget(QLabel('Resolution (px)'), grid_row, 0, Qt.AlignmentFlag.AlignRight) self.resolution_spin_box = SettingsSpinBox( - key='export_resolution', default=DEFAULT_SETTINGS['export_resolution'], + key='export_resolution', minimum=0, maximum=8192) - self.resolution_spin_box.setToolTip('Common values:\n' - '0: disable rescaling\n' - '512: SD1.5\n' - '1024: SDXL, SD3, Flux') + self.resolution_spin_box.setToolTip( + 'Common values:\n' + '0: disable rescaling\n' + '512: SD1.5\n' + '1024: SDXL, SD3, Flux') self.resolution_spin_box.textChanged.connect(self.show_megapixels) self.resolution_spin_box.textChanged.connect(self.show_statistics) grid_layout.addWidget(self.resolution_spin_box, grid_row, 1, @@ -108,11 +108,12 @@ def __init__(self, parent, image_list_model: ImageListModel): grid_layout.addWidget(QLabel('Bucket resolution size (px)'), grid_row, 0, Qt.AlignmentFlag.AlignRight) self.bucket_res_size_spin_box = SettingsSpinBox( - key='export_bucket_res_size', default=DEFAULT_SETTINGS['export_bucket_res_size'], + key='export_bucket_res_size', minimum=1, maximum=256) - self.bucket_res_size_spin_box.setToolTip('Ensure that the exported image size is divisable by that number.\n' - 'It should match the setting on the training tool.\n' - 'It might cause minor cropping.') + self.bucket_res_size_spin_box.setToolTip( + 'Ensure that the exported image size is divisable by that number.\n' + 'It should match the setting on the training tool.\n' + 'It might cause minor cropping.') self.bucket_res_size_spin_box.textChanged.connect(self.show_statistics) grid_layout.addWidget(self.bucket_res_size_spin_box, grid_row, 1, Qt.AlignmentFlag.AlignLeft) @@ -121,22 +122,21 @@ def __init__(self, parent, image_list_model: ImageListModel): grid_layout.addWidget(QLabel('Preferred sizes'), grid_row, 0, Qt.AlignmentFlag.AlignRight) self.preferred_sizes_line_edit = SettingsLineEdit( - key='export_preferred_sizes', - default=DEFAULT_SETTINGS['export_preferred_sizes']) + key='export_preferred_sizes') self.preferred_sizes_line_edit.setMinimumWidth(500) - self.preferred_sizes_line_edit.setToolTip('Comma separated list of preferred sizes and aspect ratios.\n' - "The inverse aspect ratio is automatically derived and doesn't need to be included.") + self.preferred_sizes_line_edit.setToolTip( + 'Comma separated list of preferred sizes and aspect ratios.\n' + "The inverse aspect ratio is automatically derived and doesn't need to be included.") grid_layout.addWidget(self.preferred_sizes_line_edit, grid_row, 1, Qt.AlignmentFlag.AlignLeft) grid_row += 1 grid_layout.addWidget(QLabel('Allow upscaling'), grid_row, 0, Qt.AlignmentFlag.AlignRight) - self.upscaling_check_box = SettingsBigCheckBox( - key='export_upscaling', - default=DEFAULT_SETTINGS['export_upscaling']) - self.upscaling_check_box.setToolTip('Scale too small images to the requested size.\n' - 'This should be avoided as it lowers the image quality.') + self.upscaling_check_box = SettingsBigCheckBox(key='export_upscaling') + self.upscaling_check_box.setToolTip( + 'Scale too small images to the requested size.\n' + 'This should be avoided as it lowers the image quality.') self.upscaling_check_box.stateChanged.connect(self.show_statistics) grid_layout.addWidget(self.upscaling_check_box, grid_row, 1, Qt.AlignmentFlag.AlignLeft) @@ -144,11 +144,13 @@ def __init__(self, parent, image_list_model: ImageListModel): grid_row += 1 grid_layout.addWidget(QLabel('Bucket fitting strategy'), grid_row, 0, Qt.AlignmentFlag.AlignRight) - bucket_strategy_combo_box = SettingsComboBox(key='export_bucket_strategy') + bucket_strategy_combo_box = SettingsComboBox( + key='export_bucket_strategy') bucket_strategy_combo_box.addItems(list(BucketStrategy)) - bucket_strategy_combo_box.setToolTip('crop: center crop\n' - 'scale: assymetric scaling\n' - 'crop and scale: use both to minimize each effect') + bucket_strategy_combo_box.setToolTip( + 'crop: center crop\n' + 'scale: assymetric scaling\n' + 'crop and scale: use both to minimize each effect') grid_layout.addWidget(bucket_strategy_combo_box, grid_row, 1, Qt.AlignmentFlag.AlignLeft) @@ -158,8 +160,6 @@ def __init__(self, parent, image_list_model: ImageListModel): format_widget = QWidget() format_layout = QHBoxLayout() format_layout.setContentsMargins(0, 0, 0, 0) - current_format = self.settings.value('export_format', type=str) - current_quality = self.settings.value('export_quality', type=str) self.format_combo_box = SettingsComboBox(key='export_format') self.format_combo_box.addItems(list(ExportFormat)) self.format_combo_box.currentTextChanged.connect(self.format_change) @@ -168,11 +168,12 @@ def __init__(self, parent, image_list_model: ImageListModel): format_layout.addWidget(QLabel('Quality'), Qt.AlignmentFlag.AlignRight) self.quality_spin_box = SettingsSpinBox( - key='export_quality', default=DEFAULT_SETTINGS['export_quality'], + key='export_quality', minimum=0, maximum=100) - self.quality_spin_box.setToolTip('Only for JPEG and WebP.\n' - '0 is worst and 100 is best.\n' - 'For JPEG numbers above 95 should be avoided') + self.quality_spin_box.setToolTip( + 'Only for JPEG and WebP.\n' + '0 is worst and 100 is best.\n' + 'For JPEG numbers above 95 should be avoided') self.quality_spin_box.textChanged.connect(self.quality_change) format_layout.addWidget(self.quality_spin_box, Qt.AlignmentFlag.AlignLeft) @@ -180,6 +181,8 @@ def __init__(self, parent, image_list_model: ImageListModel): grid_layout.addWidget(format_widget, grid_row, 1, Qt.AlignmentFlag.AlignLeft) # ensure correct enable/disable and background color of the quality + current_format = self.settings.value('export_format', type=str) + current_quality = self.settings.value('export_quality', type=int) self.format_change(current_format, False) self.quality_change(current_quality) @@ -190,11 +193,12 @@ def __init__(self, parent, image_list_model: ImageListModel): color_space_combo_box.addItem("feed through (don't touch)") color_space_combo_box.addItem('sRGB (implicit, without profile)') color_space_combo_box.addItems([IccProfileList[e.name] for e in QColorSpace.NamedColorSpace]) - color_space_combo_box.setToolTip('Color space of the exported images.\n' - 'Most likely the trainer expects sRGB!\n' - '\n' - 'Use "feed through" to keep the color space as it is.\n' - 'Use "sRGB (implicit, without profile)" to save in sRGB but don\'t embed the ICC profile to save 8k file size.') + color_space_combo_box.setToolTip( + 'Color space of the exported images.\n' + 'Most likely the trainer expects sRGB!\n' + '\n' + 'Use "feed through" to keep the color space as it is.\n' + 'Use "sRGB (implicit, without profile)" to save in sRGB but don\'t embed the ICC profile to save 8k file size.') grid_layout.addWidget(color_space_combo_box, grid_row, 1, Qt.AlignmentFlag.AlignLeft) @@ -202,8 +206,7 @@ def __init__(self, parent, image_list_model: ImageListModel): grid_layout.addWidget(QLabel('Export directory'), grid_row, 0, Qt.AlignmentFlag.AlignRight) self.export_directory_line_edit = SettingsLineEdit( - key='export_directory_path', - default=DEFAULT_SETTINGS['export_directory_path']) + key='export_directory_path') self.export_directory_line_edit.setMinimumWidth(500) self.export_directory_line_edit.setClearButtonEnabled(True) grid_layout.addWidget(self.export_directory_line_edit, grid_row, 1, @@ -219,10 +222,10 @@ def __init__(self, parent, image_list_model: ImageListModel): grid_layout.addWidget(QLabel('Keep input directory structure'), grid_row, 0, Qt.AlignmentFlag.AlignRight) keep_dir_structure_check_box = SettingsBigCheckBox( - key='export_keep_dir_structure', - default=DEFAULT_SETTINGS['export_keep_dir_structure']) - keep_dir_structure_check_box.setToolTip('Keep the subdirectory structure or export\n' - 'all images in the same export directory') + key='export_keep_dir_structure') + keep_dir_structure_check_box.setToolTip( + 'Keep the subdirectory structure or export\n' + 'all images in the same export directory') grid_layout.addWidget(keep_dir_structure_check_box, grid_row, 1, Qt.AlignmentFlag.AlignLeft) @@ -230,15 +233,20 @@ def __init__(self, parent, image_list_model: ImageListModel): grid_layout.addWidget(QLabel('Statistics'), grid_row, 0, Qt.AlignmentFlag.AlignRight) self.statistics_table = QTableWidget(0, 5, self) - self.statistics_table.setHorizontalHeaderLabels(['Width', 'Height', 'Count', 'Aspect ratio', 'Size utilization']) + self.statistics_table.setHorizontalHeaderLabels( + ['Width', 'Height', 'Count', 'Aspect ratio', 'Size utilization']) self.statistics_table.setMinimumWidth(500) grid_layout.addWidget(self.statistics_table, grid_row, 1, Qt.AlignmentFlag.AlignLeft) - layout.addLayout(grid_layout) + self.layout.addLayout(grid_layout) + export_button = QPushButton('Export') - export_button.clicked.connect(self.do_export) - layout.addWidget(export_button) + if self.image_list_model.rowCount() > 0: + export_button.clicked.connect(self.do_export) + else: + export_button.setEnabled(False) + self.layout.addWidget(export_button) # update display self.apply_preset(preset_combo_box.currentText(), False) @@ -252,6 +260,7 @@ def apply_preset(self, value: str, do_value_change: bool = True): Slot to call when a new preset was selected to help the user to set important settings to a consistent state. """ + inhibit_statistics_update_current = self.inhibit_statistics_update preset = Presets[value] if value == 'manual': self.resolution_spin_box.setEnabled(True) @@ -262,7 +271,7 @@ def apply_preset(self, value: str, do_value_change: bool = True): self.resolution_spin_box.setEnabled(False) self.bucket_res_size_spin_box.setValue(preset[1]) if do_value_change else 0 self.bucket_res_size_spin_box.setEnabled(False) - self.inhibit_statistics_update = False + self.inhibit_statistics_update = inhibit_statistics_update_current self.show_statistics() self.preferred_sizes_line_edit.setText(preset[2]) if do_value_change else 0 @@ -327,15 +336,18 @@ def show_statistics(self): (21, 9, 21/9), ] self.preferred_sizes = [] - for res_str in re.split(r'\s*,\s*', self.settings.value('export_preferred_sizes')): + for res_str in re.split(r'\s*,\s*', self.settings.value('export_preferred_sizes') or ''): try: + if res_str == '': + continue size_str = res_str.split(':') width = max(int(size_str[0]), int(size_str[1])) height = min(int(size_str[0]), int(size_str[1])) self.preferred_sizes.append((width, height)) if not width == height: self.preferred_sizes.append((height, width)) - # add exact aspect ratio of the preferred size to label it similar to the perfect one + # add exact aspect ratio of the preferred size to label it + # similar to the perfect one aspect_ratio = width / height for ar in aspect_ratios: if abs(ar[2] - aspect_ratio) < 0.15: @@ -343,13 +355,14 @@ def show_statistics(self): break except ValueError: # Handle cases where the resolution string is not in the correct format - print(f"Warning: Invalid resolution format: {res_str}. Skipping.") + print(f'Warning: Invalid resolution format: {res_str}. Skipping.') continue # Skip to the next resolution if there's an error image_dimensions = defaultdict(int) for image_index in range(self.image_list_model.rowCount()): this_image = self.image_list_model.index(image_index).data(Qt.ItemDataRole.UserRole) - this_image.target_dimensions = self.target_dimensions(this_image.dimensions, resolution, upscaling, bucket_res) + this_image.target_dimensions = self.target_dimensions( + this_image.dimensions, resolution, upscaling, bucket_res) image_dimensions[this_image.target_dimensions] += 1 sorted_dimensions = sorted( @@ -499,6 +512,13 @@ def do_export(self): export_directory_path = Path(self.settings.value('export_directory_path', type=str)) export_keep_dir_structure = self.settings.value('export_keep_dir_structure', type=bool) no_overwrite = True + + image_count = self.image_list_model.rowCount() + self.progress_bar = QProgressBar(self) + self.progress_bar.setMinimum(0) + self.progress_bar.setMaximum(image_count) + self.layout.addWidget(self.progress_bar) + if os.path.exists(export_directory_path): if os.path.isfile(export_directory_path): QMessageBox.critical( @@ -541,7 +561,8 @@ def do_export(self): save_profile = False bucket_strategy = self.settings.value('export_bucket_strategy', type=str) - for image_index in range(self.image_list_model.rowCount()): + for image_index in range(image_count): + self.progress_bar.setValue(image_index) image_entry = self.image_list_model.index(image_index).data(Qt.ItemDataRole.UserRole) if export_keep_dir_structure: relative_path = image_entry.path.relative_to(directory_path) diff --git a/taggui/dialogs/settings_dialog.py b/taggui/dialogs/settings_dialog.py index 8fb2956b..49d27f70 100644 --- a/taggui/dialogs/settings_dialog.py +++ b/taggui/dialogs/settings_dialog.py @@ -33,19 +33,17 @@ def __init__(self, parent): Qt.AlignmentFlag.AlignRight) font_size_spin_box = SettingsSpinBox( - key='font_size', default=DEFAULT_SETTINGS['font_size'], + key='font_size', minimum=1, maximum=99) font_size_spin_box.valueChanged.connect(self.show_restart_warning) # Images that are too small cause lag, so set a minimum width. image_list_image_width_spin_box = SettingsSpinBox( key='image_list_image_width', - default=DEFAULT_SETTINGS['image_list_image_width'], minimum=16, maximum=9999) image_list_image_width_spin_box.valueChanged.connect( self.show_restart_warning) self.insert_space_after_tag_separator_check_box = SettingsBigCheckBox( - key='insert_space_after_tag_separator', - default=DEFAULT_SETTINGS['insert_space_after_tag_separator']) + key='insert_space_after_tag_separator') self.insert_space_after_tag_separator_check_box.stateChanged.connect( self.show_restart_warning) tag_separator_line_edit = QLineEdit() @@ -60,8 +58,7 @@ def __init__(self, parent): tag_separator_line_edit.textChanged.connect( self.handle_tag_separator_change) autocomplete_tags_check_box = SettingsBigCheckBox( - key='autocomplete_tags', - default=DEFAULT_SETTINGS['autocomplete_tags']) + key='autocomplete_tags') autocomplete_tags_check_box.stateChanged.connect( self.show_restart_warning) self.models_directory_line_edit = SettingsLineEdit( @@ -76,8 +73,7 @@ def __init__(self, parent): int(models_directory_button.sizeHint().width() * 1.3)) models_directory_button.clicked.connect(self.set_models_directory_path) file_types_line_edit = SettingsLineEdit( - key='image_list_file_formats', - default=DEFAULT_SETTINGS['image_list_file_formats']) + key='image_list_file_formats') file_types_line_edit.setMinimumWidth(400) file_types_line_edit.textChanged.connect(self.show_restart_warning) diff --git a/taggui/utils/settings_widgets.py b/taggui/utils/settings_widgets.py index 43b0b5ec..3369b618 100644 --- a/taggui/utils/settings_widgets.py +++ b/taggui/utils/settings_widgets.py @@ -4,14 +4,16 @@ from utils.big_widgets import BigCheckBox from utils.focused_scroll_mixin import FocusedScrollMixin -from utils.settings import get_settings +from utils.settings import DEFAULT_SETTINGS, get_settings class SettingsBigCheckBox(BigCheckBox): - def __init__(self, key: str, default: bool, text: str | None = None): + def __init__(self, key: str, default: bool | None = None, text: str | None = None): super().__init__(text) settings = get_settings() - self.setChecked(settings.value(key, default, type=bool)) + if not settings.contains(key): + settings.setValue(key, default or DEFAULT_SETTINGS.get(key)) + self.setChecked(settings.value(key, type=bool)) self.stateChanged.connect( lambda state: settings.setValue( key, state == Qt.CheckState.Checked.value)) @@ -20,12 +22,13 @@ def __init__(self, key: str, default: bool, text: str | None = None): class SettingsComboBox(QComboBox): def __init__(self, key: str, default: str | None = None): super().__init__() - self.key = key - self.default = default self.settings = get_settings() + self.key = key + if not self.settings.contains(key): + self.settings.setValue(key, default or DEFAULT_SETTINGS.get(key)) def addItems(self, texts: list[str]): - setting: str = self.settings.value(self.key, self.default, type=str) + setting: str = self.settings.value(self.key, type=str) super().addItems(texts) self.currentTextChanged.connect( lambda text: self.settings.setValue(self.key, text)) @@ -50,11 +53,13 @@ def __init__(self, key: str, default: float, minimum: float, class SettingsSpinBox(QSpinBox): - def __init__(self, key: str, default: int, minimum: int, maximum: int): + def __init__(self, key: str, minimum: int, maximum: int, default: int | None = None): super().__init__() self.setRange(minimum, maximum) settings = get_settings() - self.setValue(settings.value(key, default, type=int)) + if not settings.contains(key): + settings.setValue(key, default or DEFAULT_SETTINGS.get(key)) + self.setValue(settings.value(key, type=int)) self.valueChanged.connect(lambda value: settings.setValue(key, value)) @@ -63,17 +68,21 @@ class FocusedScrollSettingsSpinBox(FocusedScrollMixin, SettingsSpinBox): class SettingsLineEdit(QLineEdit): - def __init__(self, key: str, default: str = ''): + def __init__(self, key: str, default: str | None = None): super().__init__() settings = get_settings() - self.setText(settings.value(key, default, type=str)) + if not settings.contains(key): + settings.setValue(key, default or DEFAULT_SETTINGS.get(key, '')) + self.setText(settings.value(key, type=str)) self.textChanged.connect(lambda text: settings.setValue(key, text)) class SettingsPlainTextEdit(QPlainTextEdit): - def __init__(self, key: str, default: str = ''): + def __init__(self, key: str, default: str | None = None): super().__init__() settings = get_settings() - self.setPlainText(settings.value(key, default, type=str)) + if not settings.contains(key): + settings.setValue(key, default or DEFAULT_SETTINGS.get(key, '')) + self.setPlainText(settings.value(key, type=str)) self.textChanged.connect(lambda: settings.setValue(key, self.toPlainText())) From 1adce1585732bd1444df5ffbec8bc3ee09aec07b Mon Sep 17 00:00:00 2001 From: StableLlama Date: Fri, 7 Feb 2025 00:08:55 +0100 Subject: [PATCH 11/82] Use new default infrastructure --- taggui/dialogs/export_dialog.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/taggui/dialogs/export_dialog.py b/taggui/dialogs/export_dialog.py index b3997bb3..13c045d8 100644 --- a/taggui/dialogs/export_dialog.py +++ b/taggui/dialogs/export_dialog.py @@ -322,9 +322,9 @@ def show_statistics(self): return self.resolution_cache = {} - resolution = self.resolution_spin_box.value() - upscaling = self.upscaling_check_box.isChecked() - bucket_res = self.bucket_res_size_spin_box.value() + resolution = self.settings.value('export_resolution', type=int) + upscaling = self.settings.value('export_upscaling', type=int) + bucket_res = self.settings.value('export_bucket_res_size', type=int) # notable aspect ratios aspect_ratios = [ @@ -549,11 +549,11 @@ def do_export(self): ) return - resolution = self.resolution_spin_box.value() - upscaling = self.upscaling_check_box.isChecked() - bucket_res = self.bucket_res_size_spin_box.value() - export_format = self.format_combo_box.currentText() - quality = self.quality_spin_box.value() + resolution = self.settings.value('export_resolution', type=int) + upscaling = self.settings.value('export_upscaling', type=int) + bucket_res = self.settings.value('export_bucket_res_size', type=int) + export_format = self.settings.value('export_format', type=str) + quality = self.settings.value('export_quality', type=int) color_space = self.settings.value('export_color_space', type=str) save_profile = True if color_space == 'sRGB (implicit, without profile)': From 3fa1021ec66c4be2d11800a8a0e7730ab23d2cf5 Mon Sep 17 00:00:00 2001 From: StableLlama Date: Fri, 7 Feb 2025 00:40:27 +0100 Subject: [PATCH 12/82] Fix broken tagging --- taggui/dialogs/export_dialog.py | 3 ++- taggui/dialogs/settings_dialog.py | 3 +-- taggui/utils/image.py | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/taggui/dialogs/export_dialog.py b/taggui/dialogs/export_dialog.py index 13c045d8..7c5d3ed3 100644 --- a/taggui/dialogs/export_dialog.py +++ b/taggui/dialogs/export_dialog.py @@ -355,7 +355,8 @@ def show_statistics(self): break except ValueError: # Handle cases where the resolution string is not in the correct format - print(f'Warning: Invalid resolution format: {res_str}. Skipping.') + print(f'Warning: Invalid resolution format: {res_str}. Skipping.', + file=sys.stderr) continue # Skip to the next resolution if there's an error image_dimensions = defaultdict(int) diff --git a/taggui/dialogs/settings_dialog.py b/taggui/dialogs/settings_dialog.py index 49d27f70..ee8d6140 100644 --- a/taggui/dialogs/settings_dialog.py +++ b/taggui/dialogs/settings_dialog.py @@ -62,8 +62,7 @@ def __init__(self, parent): autocomplete_tags_check_box.stateChanged.connect( self.show_restart_warning) self.models_directory_line_edit = SettingsLineEdit( - key='models_directory_path', - default=DEFAULT_SETTINGS['models_directory_path']) + key='models_directory_path') self.models_directory_line_edit.setMinimumWidth(400) self.models_directory_line_edit.setClearButtonEnabled(True) self.models_directory_line_edit.textChanged.connect( diff --git a/taggui/utils/image.py b/taggui/utils/image.py index 96554486..17091470 100644 --- a/taggui/utils/image.py +++ b/taggui/utils/image.py @@ -8,6 +8,6 @@ class Image: path: Path dimensions: tuple[int, int] | None - target_dimensions: tuple[int, int] | None tags: list[str] = field(default_factory=list) + target_dimensions: tuple[int, int] | None = None thumbnail: QIcon | None = None From ff0e0bedc180ae53efe5923c867d988dd758b0ec Mon Sep 17 00:00:00 2001 From: StableLlama Date: Fri, 7 Feb 2025 10:39:40 +0100 Subject: [PATCH 13/82] Finetune sharpening --- taggui/dialogs/export_dialog.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/taggui/dialogs/export_dialog.py b/taggui/dialogs/export_dialog.py index 7c5d3ed3..ba9298d7 100644 --- a/taggui/dialogs/export_dialog.py +++ b/taggui/dialogs/export_dialog.py @@ -606,7 +606,7 @@ def do_export(self): # resize with the best method available resized_image = image_file.resize((new_width, new_height), Image.LANCZOS) # followed by a slight sharpening as it should be done - sharpend_image = resized_image.filter(ImageFilter.UnsharpMask(radius = 0.5, percent = 100, threshold = 3)) + sharpend_image = resized_image.filter(ImageFilter.UnsharpMask(radius = 0.5, percent = 50, threshold = 0)) else: sharpend_image = image_file From e5325db9f07c847e304d875b983c86e32ebe4c37 Mon Sep 17 00:00:00 2001 From: StableLlama Date: Wed, 12 Feb 2025 22:30:39 +0100 Subject: [PATCH 14/82] Add export with the JPEG XL format Advantages are smaller file sizes when compression is acceptable (quality < 100) or lossless compression with quality = 100. Also the alpha channel is supported and kept in the images. Note 1: this only adds support for the export function, and is not general JPEG XL support for taggui. There is the fork https://github.com/yggdrasil75/taggui that does exactly this. Note 2: You might need `pip install pillow-jxl-plugin` beforehand to be able to export into JPEG XL. --- taggui/dialogs/export_dialog.py | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/taggui/dialogs/export_dialog.py b/taggui/dialogs/export_dialog.py index ba9298d7..4b6c0a91 100644 --- a/taggui/dialogs/export_dialog.py +++ b/taggui/dialogs/export_dialog.py @@ -20,6 +20,11 @@ SettingsSpinBox, SettingsComboBox) from models.image_list_model import ImageListModel +try: + import pillow_jxl +except ModuleNotFoundError: + pass + Presets = { 'manual': (0, 0, '1:1, 2:1, 3:2, 4:3, 16:9, 21:9'), 'Direct feed through': (0, 1, '1:1, 2:1, 3:2, 4:3, 16:9, 21:9'), @@ -29,11 +34,13 @@ class ExportFormat(str, Enum): JPG = '.jpg - JPEG' + JPGXL = '.jxl - JPEG XL' PNG = '.png - PNG' WEBP = '.webp - WEBP' ExportFormatDict = { ExportFormat.JPG: 'jpeg', + ExportFormat.JPGXL: 'jxl', ExportFormat.PNG: 'png', ExportFormat.WEBP: 'webp' } @@ -161,7 +168,12 @@ def __init__(self, parent, image_list_model: ImageListModel): format_layout = QHBoxLayout() format_layout.setContentsMargins(0, 0, 0, 0) self.format_combo_box = SettingsComboBox(key='export_format') - self.format_combo_box.addItems(list(ExportFormat)) + supported_extensions = set(Image.registered_extensions().keys()) + supported_formats = [ + format for format in ExportFormat + if any(ext in supported_extensions for ext in format.value.split(' - ')[0].split(',')) + ] + self.format_combo_box.addItems(supported_formats) self.format_combo_box.currentTextChanged.connect(self.format_change) format_layout.addWidget(self.format_combo_box, Qt.AlignmentFlag.AlignLeft) @@ -296,6 +308,9 @@ def format_change(self, export_format: ExportFormat, do_value_change: bool = Tru if export_format == ExportFormat.JPG: self.quality_spin_box.setValue(75) if do_value_change else 0 self.quality_spin_box.setEnabled(True) + if export_format == ExportFormat.JPGXL: + self.quality_spin_box.setValue(100) if do_value_change else 0 + self.quality_spin_box.setEnabled(True) elif export_format == ExportFormat.PNG: self.quality_spin_box.setValue(100) if do_value_change else 0 self.quality_spin_box.setEnabled(False) @@ -615,9 +630,10 @@ def do_export(self): crop_width = floor((current_width - image_entry.target_dimensions[0]) / 2) crop_height = floor((current_height - image_entry.target_dimensions[1]) / 2) cropped_image = sharpend_image.crop((crop_width, crop_height, current_width - crop_width, current_height - crop_height)) + lossless = quality > 99 if color_space == "feed through (don't touch)": - cropped_image.save(export_path, format=ExportFormatDict[export_format], quality=quality, icc_profile=cropped_image.info.get('icc_profile')) + cropped_image.save(export_path, format=ExportFormatDict[export_format], quality=quality, icc_profile=cropped_image.info.get('icc_profile'), lossless=lossless) else: source_profile_raw = image_file.info.get('icc_profile') if source_profile_raw is None: # assume sRGB @@ -627,7 +643,7 @@ def do_export(self): target_profile = ImageCms.ImageCmsProfile(io.BytesIO(target_profile_raw)) final_image = ImageCms.profileToProfile(cropped_image, source_profile, target_profile) if save_profile: - final_image.save(export_path, format=ExportFormatDict[export_format], quality=quality, icc_profile=target_profile.tobytes()) + final_image.save(export_path, format=ExportFormatDict[export_format], quality=quality, icc_profile=target_profile.tobytes(), lossless=lossless) else: - final_image.save(export_path, format=ExportFormatDict[export_format], quality=quality, icc_profile=None) + final_image.save(export_path, format=ExportFormatDict[export_format], quality=quality, icc_profile=None, lossless=lossless) self.close() From 6abcaa2a36b556e29d6af642336da0bd1fccacd9 Mon Sep 17 00:00:00 2001 From: StableLlama Date: Fri, 14 Feb 2025 00:04:30 +0100 Subject: [PATCH 15/82] Allow export to respect filter and selection of images --- taggui/dialogs/export_dialog.py | 61 +++++++++++++++++++++++++-------- taggui/utils/settings.py | 1 + taggui/widgets/main_window.py | 2 +- 3 files changed, 48 insertions(+), 16 deletions(-) diff --git a/taggui/dialogs/export_dialog.py b/taggui/dialogs/export_dialog.py index 4b6c0a91..c06a0056 100644 --- a/taggui/dialogs/export_dialog.py +++ b/taggui/dialogs/export_dialog.py @@ -18,13 +18,18 @@ from utils.settings import DEFAULT_SETTINGS, get_settings from utils.settings_widgets import (SettingsBigCheckBox, SettingsLineEdit, SettingsSpinBox, SettingsComboBox) -from models.image_list_model import ImageListModel +from widgets.image_list import ImageList try: import pillow_jxl except ModuleNotFoundError: pass +class ExportFilter(str, Enum): + NONE = 'All images' + FILTERED = 'Filtered images' + SELECTED = 'Selected images' + Presets = { 'manual': (0, 0, '1:1, 2:1, 3:2, 4:3, 16:9, 21:9'), 'Direct feed through': (0, 1, '1:1, 2:1, 3:2, 4:3, 16:9, 21:9'), @@ -61,12 +66,12 @@ class BucketStrategy(str, Enum): CROP_SCALE = 'crop and scale' class ExportDialog(QDialog): - def __init__(self, parent, image_list_model: ImageListModel): + def __init__(self, parent, image_list: ImageList): """ Main method to create the export dialog. """ super().__init__(parent) - self.image_list_model = image_list_model + self.image_list_view = image_list.list_view self.settings = get_settings() self.inhibit_statistics_update = True self.resolution_cache: dict[tuple, tuple] = {} @@ -80,6 +85,15 @@ def __init__(self, parent, image_list_model: ImageListModel): grid_layout.setColumnStretch(1, 1) grid_row = 0 + grid_layout.addWidget(QLabel('Image selection'), grid_row, 0, + Qt.AlignmentFlag.AlignRight) + preset_combo_box = SettingsComboBox(key='export_filter') + preset_combo_box.addItems(list(ExportFilter)) + preset_combo_box.currentTextChanged.connect(self.show_statistics) + grid_layout.addWidget(preset_combo_box, grid_row, 1, + Qt.AlignmentFlag.AlignLeft) + + grid_row += 1 grid_layout.addWidget(QLabel('Preset'), grid_row, 0, Qt.AlignmentFlag.AlignRight) preset_combo_box = SettingsComboBox(key='export_preset') @@ -253,12 +267,10 @@ def __init__(self, parent, image_list_model: ImageListModel): self.layout.addLayout(grid_layout) - export_button = QPushButton('Export') - if self.image_list_model.rowCount() > 0: - export_button.clicked.connect(self.do_export) - else: - export_button.setEnabled(False) - self.layout.addWidget(export_button) + self.export_button = QPushButton('Export') + self.export_button.clicked.connect(self.do_export) + self.export_button.setEnabled(False) + self.layout.addWidget(self.export_button) # update display self.apply_preset(preset_combo_box.currentText(), False) @@ -374,9 +386,9 @@ def show_statistics(self): file=sys.stderr) continue # Skip to the next resolution if there's an error + image_list = self.get_image_list() image_dimensions = defaultdict(int) - for image_index in range(self.image_list_model.rowCount()): - this_image = self.image_list_model.index(image_index).data(Qt.ItemDataRole.UserRole) + for this_image in image_list: this_image.target_dimensions = self.target_dimensions( this_image.dimensions, resolution, upscaling, bucket_res) image_dimensions[this_image.target_dimensions] += 1 @@ -385,6 +397,8 @@ def show_statistics(self): image_dimensions.items(), key=lambda x: x[0][0] / x[0][1] # Sort by width/height ratio ) + self.export_button.setEnabled(len(image_list) > 0) + self.statistics_table.setRowCount(0) # clear old data for dimensions, count in sorted_dimensions: @@ -529,10 +543,10 @@ def do_export(self): export_keep_dir_structure = self.settings.value('export_keep_dir_structure', type=bool) no_overwrite = True - image_count = self.image_list_model.rowCount() + image_list = self.get_image_list() self.progress_bar = QProgressBar(self) self.progress_bar.setMinimum(0) - self.progress_bar.setMaximum(image_count) + self.progress_bar.setMaximum(len(image_list)) self.layout.addWidget(self.progress_bar) if os.path.exists(export_directory_path): @@ -577,9 +591,8 @@ def do_export(self): save_profile = False bucket_strategy = self.settings.value('export_bucket_strategy', type=str) - for image_index in range(image_count): + for image_index, image_entry in enumerate(self.get_image_list()): self.progress_bar.setValue(image_index) - image_entry = self.image_list_model.index(image_index).data(Qt.ItemDataRole.UserRole) if export_keep_dir_structure: relative_path = image_entry.path.relative_to(directory_path) export_path = export_directory_path / relative_path @@ -647,3 +660,21 @@ def do_export(self): else: final_image.save(export_path, format=ExportFormatDict[export_format], quality=quality, icc_profile=None, lossless=lossless) self.close() + + def get_image_list(self): + if self.settings.value('export_filter') == ExportFilter.FILTERED: + images = self.image_list_view.proxy_image_list_model.sourceModel() + image_list = [] + for row in range(self.image_list_view.proxy_image_list_model.sourceModel().rowCount()): + source_index = self.image_list_view.proxy_image_list_model.sourceModel().index(row, 0) + proxy_index = self.image_list_view.proxy_image_list_model.mapFromSource(source_index) + if proxy_index.isValid(): + image_list.append(source_index.data(Qt.ItemDataRole.UserRole)) + elif self.settings.value('export_filter') == ExportFilter.SELECTED: + images = self.image_list_view.proxy_image_list_model.sourceModel() + image_list = [image_index.data(Qt.ItemDataRole.UserRole) for image_index in self.image_list_view.get_selected_image_indices()] + else: # ExportFilter.NONE + images = self.image_list_view.proxy_image_list_model.sourceModel() + image_list = [images.index(image_index).data(Qt.ItemDataRole.UserRole) for image_index in range(images.rowCount())] + + return image_list diff --git a/taggui/utils/settings.py b/taggui/utils/settings.py index d256d2c4..8d4ba547 100644 --- a/taggui/utils/settings.py +++ b/taggui/utils/settings.py @@ -10,6 +10,7 @@ 'insert_space_after_tag_separator': True, 'autocomplete_tags': True, 'models_directory_path': '', + 'export_filter': 'All images', 'export_preset': 'SDXL, SD3, Flux', 'export_resolution': 1024, 'export_bucket_res_size': 64, diff --git a/taggui/widgets/main_window.py b/taggui/widgets/main_window.py index cdddff32..f270e296 100644 --- a/taggui/widgets/main_window.py +++ b/taggui/widgets/main_window.py @@ -294,7 +294,7 @@ def reload_directory(self): @Slot() def export_images_dialog(self): - export_dialog = ExportDialog(parent=self, image_list_model=self.image_list_model) + export_dialog = ExportDialog(parent=self, image_list=self.image_list) export_dialog.exec() return From 1ba3949e67b9390220bbfc4271388ec6f7075a21 Mon Sep 17 00:00:00 2001 From: StableLlama Date: Fri, 14 Feb 2025 17:24:26 +0100 Subject: [PATCH 16/82] Add possibility to filter for image size --- README.md | 13 +++++++++++++ taggui/models/proxy_image_list_model.py | 10 ++++++++++ taggui/widgets/image_list.py | 4 ++-- 3 files changed, 25 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index ec25985b..19ae0fc1 100644 --- a/README.md +++ b/README.md @@ -143,6 +143,9 @@ apply: - `path`: Images that contain the filter term in the full file path - `path:cat` will match images such as `C:\Users\cats\dog.jpg` or `/home/dogs/cat.jpg`. +- `size`: Images that have the given size, stated in double colon separated + numbers. + - `size:512:512` will match images of the dimension 512x512 pixels. - You can also use a filter term with no prefix to filter for images that contain the term in either the caption or the file path. - `cat` will match images containing `cat` in the caption or file path. @@ -165,6 +168,9 @@ comparison. caption. - `tokens:<=50` will match images that have 50 or fewer tokens in the caption. +- `x` and `y`: will match images with the specified x or y dimension. + - `x:>512` will match images where the width is greater than 512 pixels. + - `y:=1024` will match images where the height is exactly 1024 pixels. ### Spaces and quotes @@ -260,6 +266,13 @@ The `Edit` menu contains additional features for batch tag operations, such as Exporting the images to a directory allows different options. By choosing the preset for the target AI model many important settings are automatically set. +`Image selection`: +Select whether all images, or those with the current filter or only the +currently selected images should be exported. + +`Preset`: +Choose a given preset or `manual` to set your own values. + `Resolution`: The native resolution of the model, like 1024 for SDXL or Flux. diff --git a/taggui/models/proxy_image_list_model.py b/taggui/models/proxy_image_list_model.py index b730c059..734ed6c6 100644 --- a/taggui/models/proxy_image_list_model.py +++ b/taggui/models/proxy_image_list_model.py @@ -37,6 +37,12 @@ def does_image_match_filter(self, image: Image, return fnmatchcase(image.path.name, f'*{filter_[1]}*') if filter_[0] == 'path': return fnmatchcase(str(image.path), f'*{filter_[1]}*') + if filter_[0] == 'size': + # accept any dimension separator of [x:] + dimension = (filter_[1]).replace(':', 'x').split('x') + return (len(dimension) == 2 + and dimension[0] == str(image.dimensions[0]) + and dimension[1] == str(image.dimensions[1])) if filter_[1] == 'AND': return (self.does_image_match_filter(image, filter_[0]) and self.does_image_match_filter(image, filter_[2:])) @@ -63,6 +69,10 @@ def does_image_match_filter(self, image: Image, caption = self.tag_separator.join(image.tags) # Subtract 2 for the `<|startoftext|>` and `<|endoftext|>` tokens. number_to_compare = len(self.tokenizer(caption).input_ids) - 2 + elif filter_[0] == 'x': + number_to_compare = image.dimensions[0] + elif filter_[0] == 'y': + number_to_compare = image.dimensions[1] return comparison_operator(number_to_compare, int(filter_[2])) def filterAcceptsRow(self, source_row: int, diff --git a/taggui/widgets/image_list.py b/taggui/widgets/image_list.py index 18af0eb0..c8dab6ad 100644 --- a/taggui/widgets/image_list.py +++ b/taggui/widgets/image_list.py @@ -48,12 +48,12 @@ def __init__(self): | QuotedString(quote_char="'", esc_char='\\') | Word(printables, exclude_chars='()')) - string_filter_keys = ['tag', 'caption', 'name', 'path'] + string_filter_keys = ['tag', 'caption', 'name', 'path', 'size'] string_filter_expressions = [Group(CaselessLiteral(key) + Suppress(':') + optionally_quoted_string) for key in string_filter_keys] comparison_operator = one_of('= == != < > <= >=') - number_filter_keys = ['tags', 'chars', 'tokens'] + number_filter_keys = ['tags', 'chars', 'tokens', 'x', 'y'] number_filter_expressions = [Group(CaselessLiteral(key) + Suppress(':') + comparison_operator + Word(nums)) for key in number_filter_keys] From 466d20bdb20be45636eb3e5c0de04f00888d88ed Mon Sep 17 00:00:00 2001 From: StableLlama Date: Sat, 15 Feb 2025 01:10:36 +0100 Subject: [PATCH 17/82] Add filtering for target size. Also refactor the code a bit to be able to use the target size calculation more easily in (future) other modules as well. --- taggui/dialogs/export_dialog.py | 161 +++++------------------- taggui/models/proxy_image_list_model.py | 10 +- taggui/utils/target_dimension.py | 143 +++++++++++++++++++++ taggui/widgets/image_list.py | 2 +- 4 files changed, 184 insertions(+), 132 deletions(-) create mode 100644 taggui/utils/target_dimension.py diff --git a/taggui/dialogs/export_dialog.py b/taggui/dialogs/export_dialog.py index c06a0056..5420a2b5 100644 --- a/taggui/dialogs/export_dialog.py +++ b/taggui/dialogs/export_dialog.py @@ -2,8 +2,7 @@ from collections import defaultdict import os import io -import re -from math import floor, sqrt +from math import floor from pathlib import Path import shutil @@ -12,12 +11,14 @@ from PySide6.QtWidgets import (QWidget, QDialog, QFileDialog, QGridLayout, QLabel, QLineEdit, QPushButton, QTableWidget, QTableWidgetItem, QProgressBar, QMessageBox, - QVBoxLayout, QHBoxLayout, QSizePolicy) + QVBoxLayout, QHBoxLayout, QSizePolicy, + QAbstractItemView) from PIL import Image, ImageFilter, ImageCms from utils.settings import DEFAULT_SETTINGS, get_settings from utils.settings_widgets import (SettingsBigCheckBox, SettingsLineEdit, SettingsSpinBox, SettingsComboBox) +import utils.target_dimension as target_dimension from widgets.image_list import ImageList try: @@ -71,10 +72,9 @@ def __init__(self, parent, image_list: ImageList): Main method to create the export dialog. """ super().__init__(parent) - self.image_list_view = image_list.list_view + self.image_list = image_list self.settings = get_settings() self.inhibit_statistics_update = True - self.resolution_cache: dict[tuple, tuple] = {} self.setWindowTitle('Export') self.layout = QVBoxLayout(self) self.layout.setContentsMargins(20, 20, 20, 20) @@ -262,6 +262,8 @@ def __init__(self, parent, image_list: ImageList): self.statistics_table.setHorizontalHeaderLabels( ['Width', 'Height', 'Count', 'Aspect ratio', 'Size utilization']) self.statistics_table.setMinimumWidth(500) + self.statistics_table.setEditTriggers(QAbstractItemView.NoEditTriggers) + self.statistics_table.itemDoubleClicked.connect(self.set_filter) grid_layout.addWidget(self.statistics_table, grid_row, 1, Qt.AlignmentFlag.AlignLeft) @@ -348,7 +350,6 @@ def show_statistics(self): if self.inhibit_statistics_update: return - self.resolution_cache = {} resolution = self.settings.value('export_resolution', type=int) upscaling = self.settings.value('export_upscaling', type=int) bucket_res = self.settings.value('export_bucket_res_size', type=int) @@ -362,36 +363,15 @@ def show_statistics(self): (16, 9, 16/9), (21, 9, 21/9), ] - self.preferred_sizes = [] - for res_str in re.split(r'\s*,\s*', self.settings.value('export_preferred_sizes') or ''): - try: - if res_str == '': - continue - size_str = res_str.split(':') - width = max(int(size_str[0]), int(size_str[1])) - height = min(int(size_str[0]), int(size_str[1])) - self.preferred_sizes.append((width, height)) - if not width == height: - self.preferred_sizes.append((height, width)) - # add exact aspect ratio of the preferred size to label it - # similar to the perfect one - aspect_ratio = width / height - for ar in aspect_ratios: - if abs(ar[2] - aspect_ratio) < 0.15: - aspect_ratios.append((ar[0], ar[1], aspect_ratio)) - break - except ValueError: - # Handle cases where the resolution string is not in the correct format - print(f'Warning: Invalid resolution format: {res_str}. Skipping.', - file=sys.stderr) - continue # Skip to the next resolution if there's an error + aspect_ratios = target_dimension.prepare(aspect_ratios) image_list = self.get_image_list() image_dimensions = defaultdict(int) for this_image in image_list: - this_image.target_dimensions = self.target_dimensions( - this_image.dimensions, resolution, upscaling, bucket_res) + this_image.target_dimensions = target_dimension.get( + this_image.dimensions) image_dimensions[this_image.target_dimensions] += 1 + self.image_list.proxy_image_list_model.invalidate() sorted_dimensions = sorted( image_dimensions.items(), @@ -420,98 +400,18 @@ def show_statistics(self): self.statistics_table.setItem(rowPosition, 3, QTableWidgetItem(f"{aspect_ratio:.3f}{notable_aspect_ratio}")) self.statistics_table.setItem(rowPosition, 4, QTableWidgetItem(f"{100*utilization:.1f}%")) - def target_dimensions(self, dimensions: tuple[int, int], resolution: int, upscaling: bool, bucket_res: int): - """ - Determine the dimensions of an image it should have when it is exported. - - Note: this gives the optimal answer and thus can be slower than the Kohya - bucket algorithm. - - Parameters - ---------- - dimensions : tuple[int, int] - The width and height of the image - resolution : int - The target resolution of the AI model. The target image pixels - will not exceed the square of this number - upscaling : bool - Is upscaling of images allowed? - bucket_res : int - The resolution of the buckets - """ - width, height = dimensions - if resolution == 0: - # no rescale in this case, only cropping - return ((width // bucket_res)*bucket_res, (height // bucket_res)*bucket_res) - - if width < bucket_res or height < bucket_res: - # it doesn't make sense to use such a small image. But we shouldn't - # patronize the user - return dimensions - - if dimensions in self.resolution_cache: - return self.resolution_cache[dimensions] - - preferred_sizes_bonus = 0.4 # reduce the loss by this factor - - max_pixels = resolution * resolution - opt_width = resolution * sqrt(width/height) - opt_height = resolution * sqrt(height/width) - if not upscaling: - opt_width = min(width, opt_width) - opt_height = min(height, opt_height) - - # test 1, guaranteed to find a solution: shrink and crop - # 1.1: exact width - candidate_width = (opt_width // bucket_res) * bucket_res - candidate_height = ((height * candidate_width / width) // bucket_res) * bucket_res - loss = ((height * candidate_width / width) - candidate_height) * candidate_width - if (candidate_width, candidate_height) in self.preferred_sizes: - loss *= preferred_sizes_bonus - # 1.2: exact height - test_height = (opt_height // bucket_res) * bucket_res - test_width = ((width * test_height / height) // bucket_res) * bucket_res - test_loss = ((width * test_height / height) - test_width) * test_height - if (test_height, test_width) in self.preferred_sizes: - test_loss *= preferred_sizes_bonus - if test_loss < loss: - candidate_width = test_width - candidate_height = test_height - loss = test_loss - - # test 2, going bigger might still fit in the size budget due to cropping - # 2.1: exact width - for delta in range(1, 10): - test_width = (opt_width // bucket_res + delta) * bucket_res - test_height = ((height * test_width / width) // bucket_res) * bucket_res - if test_width * test_height > max_pixels: - break - if (test_width > width or test_height > height) and not upscaling: - break - test_loss = ((height * test_width / width) - test_height) * test_width - if (test_height, test_width) in self.preferred_sizes: - test_loss *= preferred_sizes_bonus - if test_loss < loss: - candidate_width = test_width - candidate_height = test_height - loss = test_loss - # 2.2: exact height - for delta in range(1, 10): - test_height = (opt_height // bucket_res + delta) * bucket_res - test_width = ((width * test_height / height) // bucket_res) * bucket_res - if test_width * test_height > max_pixels: - break - if (test_width > width or test_height > height) and not upscaling: - break - test_loss = ((width * test_height / height) - test_width) * test_height - if (test_height, test_width) in self.preferred_sizes: - test_loss *= preferred_sizes_bonus - if test_loss < loss: - candidate_width = test_width - candidate_height = test_height - loss = test_loss - - return int(candidate_width), int(candidate_height) + @Slot() + def set_filter(self, selected_table_item): + row = selected_table_item.row() + width = self.statistics_table.model().index(row, 0).data() + height = self.statistics_table.model().index(row, 1).data() + filter = f'target:{width}:{height}' + text = self.image_list.filter_line_edit.text() + if text != '': + self.image_list.filter_line_edit.setText(f'{filter} AND ({text})') + else: + self.image_list.filter_line_edit.setText(filter) + self.close() @Slot() def set_export_directory_path(self): @@ -662,19 +562,20 @@ def do_export(self): self.close() def get_image_list(self): + image_list_view = self.image_list.list_view if self.settings.value('export_filter') == ExportFilter.FILTERED: - images = self.image_list_view.proxy_image_list_model.sourceModel() + images = image_list_view.proxy_image_list_model.sourceModel() image_list = [] - for row in range(self.image_list_view.proxy_image_list_model.sourceModel().rowCount()): - source_index = self.image_list_view.proxy_image_list_model.sourceModel().index(row, 0) - proxy_index = self.image_list_view.proxy_image_list_model.mapFromSource(source_index) + for row in range(image_list_view.proxy_image_list_model.sourceModel().rowCount()): + source_index = image_list_view.proxy_image_list_model.sourceModel().index(row, 0) + proxy_index = image_list_view.proxy_image_list_model.mapFromSource(source_index) if proxy_index.isValid(): image_list.append(source_index.data(Qt.ItemDataRole.UserRole)) elif self.settings.value('export_filter') == ExportFilter.SELECTED: - images = self.image_list_view.proxy_image_list_model.sourceModel() - image_list = [image_index.data(Qt.ItemDataRole.UserRole) for image_index in self.image_list_view.get_selected_image_indices()] + images = image_list_view.proxy_image_list_model.sourceModel() + image_list = [image_index.data(Qt.ItemDataRole.UserRole) for image_index in image_list_view.get_selected_image_indices()] else: # ExportFilter.NONE - images = self.image_list_view.proxy_image_list_model.sourceModel() + images = image_list_view.proxy_image_list_model.sourceModel() image_list = [images.index(image_index).data(Qt.ItemDataRole.UserRole) for image_index in range(images.rowCount())] return image_list diff --git a/taggui/models/proxy_image_list_model.py b/taggui/models/proxy_image_list_model.py index 734ed6c6..e848a90a 100644 --- a/taggui/models/proxy_image_list_model.py +++ b/taggui/models/proxy_image_list_model.py @@ -6,7 +6,7 @@ from models.image_list_model import ImageListModel from utils.image import Image - +import utils.target_dimension as target_dimension class ProxyImageListModel(QSortFilterProxyModel): def __init__(self, image_list_model: ImageListModel, @@ -43,6 +43,14 @@ def does_image_match_filter(self, image: Image, return (len(dimension) == 2 and dimension[0] == str(image.dimensions[0]) and dimension[1] == str(image.dimensions[1])) + if filter_[0] == 'target': + # accept any dimension separator of [x:] + dimension = (filter_[1]).replace(':', 'x').split('x') + if image.target_dimensions == None: + image.target_dimensions = target_dimension.get(image.dimensions) + return (len(dimension) == 2 #and image.target_dimensions != None + and dimension[0] == str(image.target_dimensions[0]) + and dimension[1] == str(image.target_dimensions[1])) if filter_[1] == 'AND': return (self.does_image_match_filter(image, filter_[0]) and self.does_image_match_filter(image, filter_[2:])) diff --git a/taggui/utils/target_dimension.py b/taggui/utils/target_dimension.py new file mode 100644 index 00000000..b7cac27b --- /dev/null +++ b/taggui/utils/target_dimension.py @@ -0,0 +1,143 @@ +from math import sqrt +import re + +from utils.settings import DEFAULT_SETTINGS, get_settings + +# singleton data store +_preferred_sizes : list[tuple[int, int]] | None = None + +def prepare(aspect_ratios : list[tuple[int, int, int]] | None = None) -> list[tuple[int, int, int]] | None: + """ + Prepare by parsing the user supplied preferred sizes. + + Parameters + ---------- + aspect_ratios : list(tuple[int, int, int]) | None + A list of typical aspect ratios to take care of + + Return + ------ + The same list of aspect ratios (when supplied) but extrended by the real + aspect ratios of the preferred sizes. + """ + global _preferred_sizes + _preferred_sizes = [] + for res_str in re.split(r'\s*,\s*', get_settings().value('export_preferred_sizes') or ''): + try: + if res_str == '': + continue + size_str = res_str.split(':') + width = max(int(size_str[0]), int(size_str[1])) + height = min(int(size_str[0]), int(size_str[1])) + _preferred_sizes.append((width, height)) + if not width == height: + _preferred_sizes.append((height, width)) + if aspect_ratios != None: + # add exact aspect ratio of the preferred size to label it + # similar to the perfect one + aspect_ratio = width / height + for ar in aspect_ratios: + if abs(ar[2] - aspect_ratio) < 0.15: + aspect_ratios.append((ar[0], ar[1], aspect_ratio)) + break + except ValueError: + # Handle cases where the resolution string is not in the correct format + print(f'Warning: Invalid resolution format: {res_str}. Skipping.', + file=sys.stderr) + continue # Skip to the next resolution if there's an error + return aspect_ratios + +def get(dimensions: tuple[int, int]): + """ + Determine the dimensions of an image it should have when it is exported. + + Note: this gives the optimal answer and thus can be slower than the Kohya + bucket algorithm. + + Parameters + ---------- + dimensions : tuple[int, int] + The width and height of the image + """ + global _preferred_sizes + width, height = dimensions + # The target resolution of the AI model. The target image pixels + # will not exceed the square of this number + resolution = get_settings().value('export_resolution', defaultValue=DEFAULT_SETTINGS['export_resolution'], type=int) + # Is upscaling of images allowed? + upscaling = get_settings().value('export_upscaling', defaultValue=DEFAULT_SETTINGS['export_upscaling'], type=bool) + # The resolution of the buckets + bucket_res = get_settings().value('export_bucket_res_size', defaultValue=DEFAULT_SETTINGS['export_bucket_res_size'], type=int) + + if not _preferred_sizes: + prepare() + + if resolution == 0: + # no rescale in this case, only cropping + return ((width // bucket_res)*bucket_res, (height // bucket_res)*bucket_res) + + if width < bucket_res or height < bucket_res: + # it doesn't make sense to use such a small image. But we shouldn't + # patronize the user + return dimensions + + preferred_sizes_bonus = 0.4 # reduce the loss by this factor + + max_pixels = resolution * resolution + opt_width = resolution * sqrt(width/height) + opt_height = resolution * sqrt(height/width) + if not upscaling: + opt_width = min(width, opt_width) + opt_height = min(height, opt_height) + + # test 1, guaranteed to find a solution: shrink and crop + # 1.1: exact width + candidate_width = (opt_width // bucket_res) * bucket_res + candidate_height = ((height * candidate_width / width) // bucket_res) * bucket_res + loss = ((height * candidate_width / width) - candidate_height) * candidate_width + if (candidate_width, candidate_height) in _preferred_sizes: + loss *= preferred_sizes_bonus + # 1.2: exact height + test_height = (opt_height // bucket_res) * bucket_res + test_width = ((width * test_height / height) // bucket_res) * bucket_res + test_loss = ((width * test_height / height) - test_width) * test_height + if (test_height, test_width) in _preferred_sizes: + test_loss *= preferred_sizes_bonus + if test_loss < loss: + candidate_width = test_width + candidate_height = test_height + loss = test_loss + + # test 2, going bigger might still fit in the size budget due to cropping + # 2.1: exact width + for delta in range(1, 10): + test_width = (opt_width // bucket_res + delta) * bucket_res + test_height = ((height * test_width / width) // bucket_res) * bucket_res + if test_width * test_height > max_pixels: + break + if (test_width > width or test_height > height) and not upscaling: + break + test_loss = ((height * test_width / width) - test_height) * test_width + if (test_height, test_width) in _preferred_sizes: + test_loss *= preferred_sizes_bonus + if test_loss < loss: + candidate_width = test_width + candidate_height = test_height + loss = test_loss + # 2.2: exact height + for delta in range(1, 10): + test_height = (opt_height // bucket_res + delta) * bucket_res + test_width = ((width * test_height / height) // bucket_res) * bucket_res + if test_width * test_height > max_pixels: + break + if (test_width > width or test_height > height) and not upscaling: + break + test_loss = ((width * test_height / height) - test_width) * test_height + if (test_height, test_width) in _preferred_sizes: + test_loss *= preferred_sizes_bonus + if test_loss < loss: + candidate_width = test_width + candidate_height = test_height + loss = test_loss + + return int(candidate_width), int(candidate_height) diff --git a/taggui/widgets/image_list.py b/taggui/widgets/image_list.py index c8dab6ad..e2b3c142 100644 --- a/taggui/widgets/image_list.py +++ b/taggui/widgets/image_list.py @@ -48,7 +48,7 @@ def __init__(self): | QuotedString(quote_char="'", esc_char='\\') | Word(printables, exclude_chars='()')) - string_filter_keys = ['tag', 'caption', 'name', 'path', 'size'] + string_filter_keys = ['tag', 'caption', 'name', 'path', 'size', 'target'] string_filter_expressions = [Group(CaselessLiteral(key) + Suppress(':') + optionally_quoted_string) for key in string_filter_keys] From 570e937f13f5bdbeec6faf50745df8f5d811e960 Mon Sep 17 00:00:00 2001 From: StableLlama Date: Sat, 15 Feb 2025 01:15:30 +0100 Subject: [PATCH 18/82] Remove little left over --- taggui/models/proxy_image_list_model.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/taggui/models/proxy_image_list_model.py b/taggui/models/proxy_image_list_model.py index e848a90a..45d6fcbe 100644 --- a/taggui/models/proxy_image_list_model.py +++ b/taggui/models/proxy_image_list_model.py @@ -48,7 +48,7 @@ def does_image_match_filter(self, image: Image, dimension = (filter_[1]).replace(':', 'x').split('x') if image.target_dimensions == None: image.target_dimensions = target_dimension.get(image.dimensions) - return (len(dimension) == 2 #and image.target_dimensions != None + return (len(dimension) == 2 and dimension[0] == str(image.target_dimensions[0]) and dimension[1] == str(image.target_dimensions[1])) if filter_[1] == 'AND': From ca6551fb37765addac33b7d4532d8ed8513e12ad Mon Sep 17 00:00:00 2001 From: StableLlama Date: Sat, 15 Feb 2025 11:37:26 +0100 Subject: [PATCH 19/82] Add option to only export missing images --- taggui/dialogs/export_dialog.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/taggui/dialogs/export_dialog.py b/taggui/dialogs/export_dialog.py index 5420a2b5..7ecad394 100644 --- a/taggui/dialogs/export_dialog.py +++ b/taggui/dialogs/export_dialog.py @@ -442,6 +442,7 @@ def do_export(self): export_directory_path = Path(self.settings.value('export_directory_path', type=str)) export_keep_dir_structure = self.settings.value('export_keep_dir_structure', type=bool) no_overwrite = True + only_missing = True image_list = self.get_image_list() self.progress_bar = QProgressBar(self) @@ -462,8 +463,9 @@ def do_export(self): msgBox.setIcon(QMessageBox.Warning) msgBox.setWindowTitle('Path warning') msgBox.setText('The export directory path is not empty') - overwrite_button = msgBox.addButton('Overwrite', QMessageBox.YesRole) - rename_button = msgBox.addButton('Rename', QMessageBox.NoRole) + overwrite_button = msgBox.addButton('Overwrite', QMessageBox.DestructiveRole) + rename_button = msgBox.addButton('Rename', QMessageBox.YesRole) + only_missing_button = msgBox.addButton('Only missing', QMessageBox.AcceptRole) msgBox.addButton(QMessageBox.Cancel) msgBox.setDefaultButton(QMessageBox.Cancel) button = msgBox.exec_() @@ -471,6 +473,9 @@ def do_export(self): return if msgBox.clickedButton() == overwrite_button: no_overwrite = False + only_missing = False + if msgBox.clickedButton() == rename_button: + only_missing = False else: QMessageBox.critical( self, @@ -501,6 +506,9 @@ def do_export(self): export_path = export_directory_path / image_entry.path.name export_path = export_path.with_suffix(export_format.split(' ', 1)[0]) + if export_path.exists() and only_missing: + continue + if no_overwrite: stem = export_path.stem counter = 0 From 5c020b07f425e7e41dd42a57354190d6295d28a6 Mon Sep 17 00:00:00 2001 From: StableLlama Date: Thu, 20 Feb 2025 22:24:21 +0100 Subject: [PATCH 20/82] Change image_viewer to show rectangles and edit them. Also change the Settings infrastructure to send a Signal when a setting is changed. --- taggui/dialogs/export_dialog.py | 43 +- taggui/dialogs/find_and_replace_dialog.py | 2 - taggui/dialogs/settings_dialog.py | 13 +- taggui/models/image_list_model.py | 3 +- taggui/run_gui.py | 3 +- taggui/utils/big_widgets.py | 3 +- taggui/utils/image.py | 2 + taggui/utils/settings.py | 19 +- taggui/utils/settings_widgets.py | 16 +- taggui/utils/target_dimension.py | 26 +- taggui/widgets/auto_captioner.py | 8 +- taggui/widgets/image_list.py | 4 +- taggui/widgets/image_tags_editor.py | 3 +- taggui/widgets/image_viewer.py | 488 +++++++++++++--------- taggui/widgets/main_window.py | 33 +- 15 files changed, 378 insertions(+), 288 deletions(-) diff --git a/taggui/dialogs/export_dialog.py b/taggui/dialogs/export_dialog.py index 7ecad394..736a4637 100644 --- a/taggui/dialogs/export_dialog.py +++ b/taggui/dialogs/export_dialog.py @@ -15,7 +15,7 @@ QAbstractItemView) from PIL import Image, ImageFilter, ImageCms -from utils.settings import DEFAULT_SETTINGS, get_settings +from utils.settings import DEFAULT_SETTINGS, settings from utils.settings_widgets import (SettingsBigCheckBox, SettingsLineEdit, SettingsSpinBox, SettingsComboBox) import utils.target_dimension as target_dimension @@ -73,7 +73,6 @@ def __init__(self, parent, image_list: ImageList): """ super().__init__(parent) self.image_list = image_list - self.settings = get_settings() self.inhibit_statistics_update = True self.setWindowTitle('Export') self.layout = QVBoxLayout(self) @@ -207,8 +206,8 @@ def __init__(self, parent, image_list: ImageList): grid_layout.addWidget(format_widget, grid_row, 1, Qt.AlignmentFlag.AlignLeft) # ensure correct enable/disable and background color of the quality - current_format = self.settings.value('export_format', type=str) - current_quality = self.settings.value('export_quality', type=int) + current_format = settings.value('export_format', type=str) + current_quality = settings.value('export_quality', type=int) self.format_change(current_format, False) self.quality_change(current_quality) @@ -350,9 +349,9 @@ def show_statistics(self): if self.inhibit_statistics_update: return - resolution = self.settings.value('export_resolution', type=int) - upscaling = self.settings.value('export_upscaling', type=int) - bucket_res = self.settings.value('export_bucket_res_size', type=int) + resolution = settings.value('export_resolution', type=int) + upscaling = settings.value('export_upscaling', type=int) + bucket_res = settings.value('export_bucket_res_size', type=int) # notable aspect ratios aspect_ratios = [ @@ -418,13 +417,13 @@ def set_export_directory_path(self): """ Set the path of the directory to export to. """ - export_directory_path = self.settings.value( + export_directory_path = settings.value( 'export_directory_path', defaultValue=DEFAULT_SETTINGS['export_directory_path'], type=str) if export_directory_path: initial_directory_path = export_directory_path - elif self.settings.contains('directory_path'): - initial_directory_path = self.settings.value('directory_path') + elif settings.contains('directory_path'): + initial_directory_path = settings.value('directory_path') else: initial_directory_path = '' export_directory_path = QFileDialog.getExistingDirectory( @@ -438,9 +437,9 @@ def do_export(self): """ Export all images with the configured settings. """ - directory_path = self.settings.value('directory_path', type=str) - export_directory_path = Path(self.settings.value('export_directory_path', type=str)) - export_keep_dir_structure = self.settings.value('export_keep_dir_structure', type=bool) + directory_path = settings.value('directory_path', type=str) + export_directory_path = Path(settings.value('export_directory_path', type=str)) + export_keep_dir_structure = settings.value('export_keep_dir_structure', type=bool) no_overwrite = True only_missing = True @@ -484,17 +483,17 @@ def do_export(self): ) return - resolution = self.settings.value('export_resolution', type=int) - upscaling = self.settings.value('export_upscaling', type=int) - bucket_res = self.settings.value('export_bucket_res_size', type=int) - export_format = self.settings.value('export_format', type=str) - quality = self.settings.value('export_quality', type=int) - color_space = self.settings.value('export_color_space', type=str) + resolution = settings.value('export_resolution', type=int) + upscaling = settings.value('export_upscaling', type=int) + bucket_res = settings.value('export_bucket_res_size', type=int) + export_format = settings.value('export_format', type=str) + quality = settings.value('export_quality', type=int) + color_space = settings.value('export_color_space', type=str) save_profile = True if color_space == 'sRGB (implicit, without profile)': color_space = 'sRGB' save_profile = False - bucket_strategy = self.settings.value('export_bucket_strategy', type=str) + bucket_strategy = settings.value('export_bucket_strategy', type=str) for image_index, image_entry in enumerate(self.get_image_list()): self.progress_bar.setValue(image_index) @@ -571,7 +570,7 @@ def do_export(self): def get_image_list(self): image_list_view = self.image_list.list_view - if self.settings.value('export_filter') == ExportFilter.FILTERED: + if settings.value('export_filter') == ExportFilter.FILTERED: images = image_list_view.proxy_image_list_model.sourceModel() image_list = [] for row in range(image_list_view.proxy_image_list_model.sourceModel().rowCount()): @@ -579,7 +578,7 @@ def get_image_list(self): proxy_index = image_list_view.proxy_image_list_model.mapFromSource(source_index) if proxy_index.isValid(): image_list.append(source_index.data(Qt.ItemDataRole.UserRole)) - elif self.settings.value('export_filter') == ExportFilter.SELECTED: + elif settings.value('export_filter') == ExportFilter.SELECTED: images = image_list_view.proxy_image_list_model.sourceModel() image_list = [image_index.data(Qt.ItemDataRole.UserRole) for image_index in image_list_view.get_selected_image_indices()] else: # ExportFilter.NONE diff --git a/taggui/dialogs/find_and_replace_dialog.py b/taggui/dialogs/find_and_replace_dialog.py index f61f9e0e..3d8dc0f5 100644 --- a/taggui/dialogs/find_and_replace_dialog.py +++ b/taggui/dialogs/find_and_replace_dialog.py @@ -5,7 +5,6 @@ QVBoxLayout) from models.image_list_model import ImageListModel, Scope -from utils.settings import get_settings from utils.settings_widgets import (SettingsBigCheckBox, SettingsComboBox, SettingsLineEdit) from utils.utils import pluralize @@ -15,7 +14,6 @@ class FindAndReplaceDialog(QDialog): def __init__(self, parent, image_list_model: ImageListModel): super().__init__(parent) self.image_list_model = image_list_model - self.settings = get_settings() self.setWindowTitle('Find and Replace') layout = QVBoxLayout(self) layout.setContentsMargins(20, 20, 20, 20) diff --git a/taggui/dialogs/settings_dialog.py b/taggui/dialogs/settings_dialog.py index ee8d6140..4541865e 100644 --- a/taggui/dialogs/settings_dialog.py +++ b/taggui/dialogs/settings_dialog.py @@ -2,7 +2,7 @@ from PySide6.QtWidgets import (QDialog, QFileDialog, QGridLayout, QLabel, QLineEdit, QPushButton, QVBoxLayout) -from utils.settings import DEFAULT_SETTINGS, get_settings +from utils.settings import DEFAULT_SETTINGS, settings from utils.settings_widgets import (SettingsBigCheckBox, SettingsLineEdit, SettingsSpinBox) @@ -10,7 +10,6 @@ class SettingsDialog(QDialog): def __init__(self, parent): super().__init__(parent) - self.settings = get_settings() self.setWindowTitle('Settings') layout = QVBoxLayout(self) layout.setContentsMargins(20, 20, 20, 20) @@ -47,7 +46,7 @@ def __init__(self, parent): self.insert_space_after_tag_separator_check_box.stateChanged.connect( self.show_restart_warning) tag_separator_line_edit = QLineEdit() - tag_separator = self.settings.value( + tag_separator = settings.value( 'tag_separator', defaultValue=DEFAULT_SETTINGS['tag_separator'], type=str) if tag_separator == '\n': @@ -128,18 +127,18 @@ def handle_tag_separator_change(self, tag_separator: str): self.disable_insert_space_after_tag_separator_check_box() else: self.insert_space_after_tag_separator_check_box.setEnabled(True) - self.settings.setValue('tag_separator', tag_separator) + settings.setValue('tag_separator', tag_separator) self.show_restart_warning() @Slot() def set_models_directory_path(self): - models_directory_path = self.settings.value( + models_directory_path = settings.value( 'models_directory_path', defaultValue=DEFAULT_SETTINGS['models_directory_path'], type=str) if models_directory_path: initial_directory_path = models_directory_path - elif self.settings.contains('directory_path'): - initial_directory_path = self.settings.value('directory_path') + elif settings.contains('directory_path'): + initial_directory_path = settings.value('directory_path') else: initial_directory_path = '' models_directory_path = QFileDialog.getExistingDirectory( diff --git a/taggui/models/image_list_model.py b/taggui/models/image_list_model.py index 09e9127d..58992fd9 100644 --- a/taggui/models/image_list_model.py +++ b/taggui/models/image_list_model.py @@ -14,7 +14,7 @@ from PySide6.QtWidgets import QMessageBox from utils.image import Image -from utils.settings import DEFAULT_SETTINGS, get_settings +from utils.settings import DEFAULT_SETTINGS, settings from utils.utils import get_confirmation_dialog_reply, pluralize UNDO_STACK_SIZE = 32 @@ -106,7 +106,6 @@ def load_directory(self, directory_path: Path): self.redo_stack.clear() self.update_undo_and_redo_actions_requested.emit() file_paths = get_file_paths(directory_path) - settings = get_settings() image_suffixes_string = settings.value( 'image_list_file_formats', defaultValue=DEFAULT_SETTINGS['image_list_file_formats'], type=str) diff --git a/taggui/run_gui.py b/taggui/run_gui.py index 46b74a8c..5f19d722 100644 --- a/taggui/run_gui.py +++ b/taggui/run_gui.py @@ -8,7 +8,7 @@ from PySide6.QtGui import QImageReader from PySide6.QtWidgets import QApplication, QMessageBox -from utils.settings import get_settings +from utils.settings import settings from widgets.main_window import MainWindow @@ -49,7 +49,6 @@ def run_gui(): try: run_gui() except Exception as exception: - settings = get_settings() settings.clear() error_message_box = QMessageBox() error_message_box.setWindowTitle('Error') diff --git a/taggui/utils/big_widgets.py b/taggui/utils/big_widgets.py index 098026bf..b0c7a5ab 100644 --- a/taggui/utils/big_widgets.py +++ b/taggui/utils/big_widgets.py @@ -1,6 +1,6 @@ from PySide6.QtWidgets import QCheckBox, QPushButton -from utils.settings import DEFAULT_SETTINGS, get_settings +from utils.settings import DEFAULT_SETTINGS, settings class BigPushButton(QPushButton): @@ -20,7 +20,6 @@ def __init__(self, text: str): class BigCheckBox(QCheckBox): def __init__(self, text: str | None = None): super().__init__(text) - settings = get_settings() font_size = settings.value( 'font_size', defaultValue=DEFAULT_SETTINGS['font_size'], type=int) new_size = font_size * 1.5 diff --git a/taggui/utils/image.py b/taggui/utils/image.py index 17091470..e01991ad 100644 --- a/taggui/utils/image.py +++ b/taggui/utils/image.py @@ -10,4 +10,6 @@ class Image: dimensions: tuple[int, int] | None tags: list[str] = field(default_factory=list) target_dimensions: tuple[int, int] | None = None + # store for each crop (x, y, width, height) the target dimension (width, heiht) + crops: dict[tuple[int, int, int, int], tuple[int, int]] | None = None thumbnail: QIcon | None = None diff --git a/taggui/utils/settings.py b/taggui/utils/settings.py index 8d4ba547..19dce534 100644 --- a/taggui/utils/settings.py +++ b/taggui/utils/settings.py @@ -1,4 +1,4 @@ -from PySide6.QtCore import QSettings +from PySide6.QtCore import QSettings, Signal # Defaults for settings that are accessed from multiple places. DEFAULT_SETTINGS = { @@ -25,13 +25,22 @@ } -def get_settings() -> QSettings: - settings = QSettings('taggui', 'taggui') - return settings +class Settings(QSettings): + # Signal that shows that the setting with the given string was changes + change = Signal(str, object, name='settingsChanged') + + def __init__(self): + super().__init__('taggui', 'taggui') + + def setValue(self, key, value): + super().setValue(key, value) + self.change.emit(key, value) + +# Common shared instance to ensure the Signal is also shared +settings = Settings() def get_tag_separator() -> str: - settings = get_settings() tag_separator = settings.value( 'tag_separator', defaultValue=DEFAULT_SETTINGS['tag_separator'], type=str) diff --git a/taggui/utils/settings_widgets.py b/taggui/utils/settings_widgets.py index 3369b618..3c5ab8bf 100644 --- a/taggui/utils/settings_widgets.py +++ b/taggui/utils/settings_widgets.py @@ -4,13 +4,12 @@ from utils.big_widgets import BigCheckBox from utils.focused_scroll_mixin import FocusedScrollMixin -from utils.settings import DEFAULT_SETTINGS, get_settings +from utils.settings import DEFAULT_SETTINGS, settings class SettingsBigCheckBox(BigCheckBox): def __init__(self, key: str, default: bool | None = None, text: str | None = None): super().__init__(text) - settings = get_settings() if not settings.contains(key): settings.setValue(key, default or DEFAULT_SETTINGS.get(key)) self.setChecked(settings.value(key, type=bool)) @@ -22,16 +21,15 @@ def __init__(self, key: str, default: bool | None = None, text: str | None = Non class SettingsComboBox(QComboBox): def __init__(self, key: str, default: str | None = None): super().__init__() - self.settings = get_settings() self.key = key - if not self.settings.contains(key): - self.settings.setValue(key, default or DEFAULT_SETTINGS.get(key)) + if not settings.contains(key): + settings.setValue(key, default or DEFAULT_SETTINGS.get(key)) def addItems(self, texts: list[str]): - setting: str = self.settings.value(self.key, type=str) + setting: str = settings.value(self.key, type=str) super().addItems(texts) self.currentTextChanged.connect( - lambda text: self.settings.setValue(self.key, text)) + lambda text: settings.setValue(self.key, text)) if setting: self.setCurrentText(setting) @@ -47,7 +45,6 @@ def __init__(self, key: str, default: float, minimum: float, # The range must be set here so that the setting value is not clamped # by the default range. self.setRange(minimum, maximum) - settings = get_settings() self.setValue(settings.value(key, default, type=float)) self.valueChanged.connect(lambda value: settings.setValue(key, value)) @@ -56,7 +53,6 @@ class SettingsSpinBox(QSpinBox): def __init__(self, key: str, minimum: int, maximum: int, default: int | None = None): super().__init__() self.setRange(minimum, maximum) - settings = get_settings() if not settings.contains(key): settings.setValue(key, default or DEFAULT_SETTINGS.get(key)) self.setValue(settings.value(key, type=int)) @@ -70,7 +66,6 @@ class FocusedScrollSettingsSpinBox(FocusedScrollMixin, SettingsSpinBox): class SettingsLineEdit(QLineEdit): def __init__(self, key: str, default: str | None = None): super().__init__() - settings = get_settings() if not settings.contains(key): settings.setValue(key, default or DEFAULT_SETTINGS.get(key, '')) self.setText(settings.value(key, type=str)) @@ -80,7 +75,6 @@ def __init__(self, key: str, default: str | None = None): class SettingsPlainTextEdit(QPlainTextEdit): def __init__(self, key: str, default: str | None = None): super().__init__() - settings = get_settings() if not settings.contains(key): settings.setValue(key, default or DEFAULT_SETTINGS.get(key, '')) self.setPlainText(settings.value(key, type=str)) diff --git a/taggui/utils/target_dimension.py b/taggui/utils/target_dimension.py index b7cac27b..ca64a5f8 100644 --- a/taggui/utils/target_dimension.py +++ b/taggui/utils/target_dimension.py @@ -1,7 +1,7 @@ from math import sqrt import re -from utils.settings import DEFAULT_SETTINGS, get_settings +from utils.settings import DEFAULT_SETTINGS, settings # singleton data store _preferred_sizes : list[tuple[int, int]] | None = None @@ -22,7 +22,7 @@ def prepare(aspect_ratios : list[tuple[int, int, int]] | None = None) -> list[tu """ global _preferred_sizes _preferred_sizes = [] - for res_str in re.split(r'\s*,\s*', get_settings().value('export_preferred_sizes') or ''): + for res_str in re.split(r'\s*,\s*', settings.value('export_preferred_sizes') or ''): try: if res_str == '': continue @@ -63,11 +63,11 @@ def get(dimensions: tuple[int, int]): width, height = dimensions # The target resolution of the AI model. The target image pixels # will not exceed the square of this number - resolution = get_settings().value('export_resolution', defaultValue=DEFAULT_SETTINGS['export_resolution'], type=int) + resolution = settings.value('export_resolution', defaultValue=DEFAULT_SETTINGS['export_resolution'], type=int) # Is upscaling of images allowed? - upscaling = get_settings().value('export_upscaling', defaultValue=DEFAULT_SETTINGS['export_upscaling'], type=bool) + upscaling = settings.value('export_upscaling', defaultValue=DEFAULT_SETTINGS['export_upscaling'], type=bool) # The resolution of the buckets - bucket_res = get_settings().value('export_bucket_res_size', defaultValue=DEFAULT_SETTINGS['export_bucket_res_size'], type=int) + bucket_res = settings.value('export_bucket_res_size', defaultValue=DEFAULT_SETTINGS['export_bucket_res_size'], type=int) if not _preferred_sizes: prepare() @@ -92,14 +92,14 @@ def get(dimensions: tuple[int, int]): # test 1, guaranteed to find a solution: shrink and crop # 1.1: exact width - candidate_width = (opt_width // bucket_res) * bucket_res - candidate_height = ((height * candidate_width / width) // bucket_res) * bucket_res + candidate_width = max(opt_width // bucket_res, 1) * bucket_res + candidate_height = max((height * candidate_width / width) // bucket_res, 1) * bucket_res loss = ((height * candidate_width / width) - candidate_height) * candidate_width if (candidate_width, candidate_height) in _preferred_sizes: loss *= preferred_sizes_bonus # 1.2: exact height - test_height = (opt_height // bucket_res) * bucket_res - test_width = ((width * test_height / height) // bucket_res) * bucket_res + test_height = max(opt_height // bucket_res, 1) * bucket_res + test_width = max((width * test_height / height) // bucket_res, 1) * bucket_res test_loss = ((width * test_height / height) - test_width) * test_height if (test_height, test_width) in _preferred_sizes: test_loss *= preferred_sizes_bonus @@ -111,8 +111,8 @@ def get(dimensions: tuple[int, int]): # test 2, going bigger might still fit in the size budget due to cropping # 2.1: exact width for delta in range(1, 10): - test_width = (opt_width // bucket_res + delta) * bucket_res - test_height = ((height * test_width / width) // bucket_res) * bucket_res + test_width = max(opt_width // bucket_res + delta, 1) * bucket_res + test_height = max((height * test_width / width) // bucket_res, 1) * bucket_res if test_width * test_height > max_pixels: break if (test_width > width or test_height > height) and not upscaling: @@ -126,8 +126,8 @@ def get(dimensions: tuple[int, int]): loss = test_loss # 2.2: exact height for delta in range(1, 10): - test_height = (opt_height // bucket_res + delta) * bucket_res - test_width = ((width * test_height / height) // bucket_res) * bucket_res + test_height = max(opt_height // bucket_res + delta, 1) * bucket_res + test_width = max((width * test_height / height) // bucket_res, 1) * bucket_res if test_width * test_height > max_pixels: break if (test_width > width or test_height > height) and not upscaling: diff --git a/taggui/widgets/auto_captioner.py b/taggui/widgets/auto_captioner.py index 58935904..c8486c5b 100644 --- a/taggui/widgets/auto_captioner.py +++ b/taggui/widgets/auto_captioner.py @@ -15,7 +15,7 @@ from models.image_list_model import ImageListModel from utils.big_widgets import TallPushButton from utils.enums import CaptionDevice, CaptionPosition -from utils.settings import DEFAULT_SETTINGS, get_settings, get_tag_separator +from utils.settings import DEFAULT_SETTINGS, settings, get_tag_separator from utils.settings_widgets import (FocusedScrollSettingsComboBox, FocusedScrollSettingsDoubleSpinBox, FocusedScrollSettingsSpinBox, @@ -50,7 +50,6 @@ def __init__(self): class CaptionSettingsForm(QVBoxLayout): def __init__(self): super().__init__() - self.settings = get_settings() try: import bitsandbytes self.is_bitsandbytes_available = True @@ -241,7 +240,7 @@ def __init__(self): self.load_in_4_bit_check_box.setChecked(False) def get_local_model_paths(self) -> list[str]: - models_directory_path = self.settings.value( + models_directory_path = settings.value( 'models_directory_path', defaultValue=DEFAULT_SETTINGS['models_directory_path'], type=str) if not models_directory_path: @@ -357,7 +356,6 @@ def __init__(self, image_list_model: ImageListModel, super().__init__() self.image_list_model = image_list_model self.image_list = image_list - self.settings = get_settings() self.is_captioning = False self.captioning_thread = None self.processor = None @@ -482,7 +480,7 @@ def generate_captions(self): self.progress_bar.setValue(0) self.progress_bar.show() tag_separator = get_tag_separator() - models_directory_path = self.settings.value( + models_directory_path = settings.value( 'models_directory_path', defaultValue=DEFAULT_SETTINGS['models_directory_path'], type=str) models_directory_path = (Path(models_directory_path) diff --git a/taggui/widgets/image_list.py b/taggui/widgets/image_list.py index e2b3c142..f51fdbab 100644 --- a/taggui/widgets/image_list.py +++ b/taggui/widgets/image_list.py @@ -18,7 +18,7 @@ from models.proxy_image_list_model import ProxyImageListModel from utils.image import Image -from utils.settings import get_settings +from utils.settings import settings from utils.settings_widgets import SettingsComboBox from utils.utils import get_confirmation_dialog_reply, pluralize @@ -235,7 +235,6 @@ def move_selected_images(self): caption = (f'Select directory to move {selected_image_count} selected ' f'{pluralize("Image", selected_image_count)} and ' f'{pluralize("caption", selected_image_count)} to') - settings = get_settings() move_directory_path = QFileDialog.getExistingDirectory( parent=self, caption=caption, dir=settings.value('directory_path', type=str)) @@ -262,7 +261,6 @@ def copy_selected_images(self): caption = (f'Select directory to copy {selected_image_count} selected ' f'{pluralize("Image", selected_image_count)} and ' f'{pluralize("caption", selected_image_count)} to') - settings = get_settings() copy_directory_path = QFileDialog.getExistingDirectory( parent=self, caption=caption, dir=settings.value('directory_path', type=str)) diff --git a/taggui/widgets/image_tags_editor.py b/taggui/widgets/image_tags_editor.py index 65a85e05..d7b05427 100644 --- a/taggui/widgets/image_tags_editor.py +++ b/taggui/widgets/image_tags_editor.py @@ -9,7 +9,7 @@ from models.proxy_image_list_model import ProxyImageListModel from models.tag_counter_model import TagCounterModel from utils.image import Image -from utils.settings import DEFAULT_SETTINGS, get_settings +from utils.settings import DEFAULT_SETTINGS, settings from utils.text_edit_item_delegate import TextEditItemDelegate from utils.utils import get_confirmation_dialog_reply from widgets.image_list import ImageList @@ -30,7 +30,6 @@ def __init__(self, image_tag_list_model: QStringListModel, self.setPlaceholderText('Add Tag') self.setStyleSheet('padding: 8px;') - settings = get_settings() autocomplete_tags = settings.value( 'autocomplete_tags', defaultValue=DEFAULT_SETTINGS['autocomplete_tags'], type=bool) diff --git a/taggui/widgets/image_viewer.py b/taggui/widgets/image_viewer.py index b15cc9ab..ca9ac12b 100644 --- a/taggui/widgets/image_viewer.py +++ b/taggui/widgets/image_viewer.py @@ -1,90 +1,226 @@ +from enum import Enum +from math import floor from pathlib import Path -from PySide6.QtCore import QModelIndex, QPoint, QPointF, QRect, QSize, Qt, Signal, Slot, QEvent -from PySide6.QtGui import QCursor, QImageReader, QMouseEvent, QPixmap, QResizeEvent, QWheelEvent -from PySide6.QtWidgets import (QFrame, QLabel, QScrollArea, QSizePolicy, QVBoxLayout, - QWidget) - +from PySide6.QtCore import (QModelIndex, QPoint, QPointF, QRect, QRectF, QSize, + QSizeF, Qt, Signal, Slot, QEvent) +from PySide6.QtGui import (QCursor, QColor, QPainter, QPainterPath, QPen, + QPixmap, QTransform) +from PySide6.QtWidgets import (QGraphicsItem, QGraphicsPixmapItem, + QGraphicsRectItem, QGraphicsScene, QGraphicsView, + QVBoxLayout, QWidget) +from utils.settings import settings from models.proxy_image_list_model import ProxyImageListModel from utils.image import Image +import utils.target_dimension as target_dimension +from dialogs.export_dialog import BucketStrategy + +class ImageMarking(str, Enum): + CROP = 'crop' + HINT = 'hint' + INCLUDE = 'include in mask' + EXCLUDE = 'exclude from mask' + +class RectPosition(str, Enum): + TL = 'top left' + TOP = 'top' + TR = 'top right' + RIGHT = 'right' + BR = 'bottom right' + BOTTOM = 'bottom' + BL = 'bottom left' + LEFT = 'left' + +class CustomRectItem(QGraphicsRectItem): + # the halfed size of the pen in local coordinates to make sure it stays the + # same during zooming + pen_half_width = 1.0 + # the minimal size of the active area in scene coordinates + handle_half_size = 5 + zoom_factor = 1.0 + # The size of the image this rect belongs to + image_size = QRectF(0, 0, 1, 1) + + def __init__(self, rect: QRect, rect_type: ImageMarking, target_size: QSize=None, parent=None): + super().__init__(rect.toRectF(), parent) + self.setFlag(QGraphicsItem.ItemIsSelectable, True) + self.setFlag(QGraphicsItem.ItemSendsScenePositionChanges, True) + self.setAcceptHoverEvents(True) + self.rect_type = rect_type + self.color = { + ImageMarking.CROP: Qt.blue, + ImageMarking.HINT: Qt.gray, + ImageMarking.INCLUDE: Qt.green, + ImageMarking.EXCLUDE: Qt.red, + }[rect_type] + self.target_size = target_size + self.handle_selected = None + self.mouse_press_pos = None + self.mouse_press_rect = None + + def change_pen_half_width(self): + self.prepareGeometryChange() + + def handleAt(self, point: QPointF) -> RectPosition | None: + handle_space = -min(self.pen_half_width - self.handle_half_size, 0)/self.zoom_factor + left = point.x() < self.rect().left() + handle_space + right = point.x() > self.rect().right() - handle_space + top = point.y() < self.rect().top() + handle_space + bottom = point.y() > self.rect().bottom() - handle_space + if top: + if left: + return RectPosition.TL + elif right: + return RectPosition.TR + return RectPosition.TOP + elif bottom: + if left: + return RectPosition.BL + elif right: + return RectPosition.BR + return RectPosition.BOTTOM + if left: + return RectPosition.LEFT + elif right: + return RectPosition.RIGHT + + return None + + def hoverMoveEvent(self, event): + handle = self.handleAt(event.pos()) + if handle == RectPosition.TL or handle == RectPosition.BR: + self.setCursor(Qt.SizeFDiagCursor) + elif handle == RectPosition.TR or handle == RectPosition.BL: + self.setCursor(Qt.SizeBDiagCursor) + elif handle == RectPosition.TOP or handle == RectPosition.BOTTOM: + self.setCursor(Qt.SizeVerCursor) + elif handle == RectPosition.LEFT or handle == RectPosition.RIGHT: + self.setCursor(Qt.SizeHorCursor) + else: + self.setCursor(Qt.OpenHandCursor) + event.ignore() + super().hoverMoveEvent(event) + + def mousePressEvent(self, event): + self.handle_selected = self.handleAt(event.pos()) + if self.handle_selected: + self.mouse_press_pos = event.pos() + self.mouse_press_scene_pos = event.scenePos() + self.mouse_press_rect = self.rect() + else: + event.ignore() + super().mousePressEvent(event) + + def mouseMoveEvent(self, event): + if self.handle_selected: + rect = self.rect() + pos_quantizised = event.scenePos().toPoint().toPointF() + if self.handle_selected == RectPosition.TL: + rect.setTopLeft(pos_quantizised) + if rect.width() < 0 and rect.height() < 0: + self.handle_selected = RectPosition.BR + elif rect.width() < 0: + self.handle_selected = RectPosition.TR + elif rect.height() < 0: + self.handle_selected = RectPosition.BL + elif self.handle_selected == RectPosition.TOP: + rect.setTop(pos_quantizised.y()) + if rect.height() < 0: + self.handle_selected = RectPosition.BOTTOM + elif self.handle_selected == RectPosition.TR: + rect.setTopRight(pos_quantizised) + if rect.width() < 0 and rect.height() < 0: + self.handle_selected = RectPosition.BL + elif rect.width() < 0: + self.handle_selected = RectPosition.TL + elif rect.height() < 0: + self.handle_selected = RectPosition.BR + elif self.handle_selected == RectPosition.RIGHT: + rect.setRight(pos_quantizised.x()) + if rect.width() < 0: + self.handle_selected = RectPosition.LEFT + elif self.handle_selected == RectPosition.BR: + rect.setBottomRight(pos_quantizised) + if rect.width() < 0 and rect.height() < 0: + self.handle_selected = RectPosition.TL + elif rect.width() < 0: + self.handle_selected = RectPosition.BL + elif rect.height() < 0: + self.handle_selected = RectPosition.TR + elif self.handle_selected == RectPosition.BOTTOM: + rect.setBottom(pos_quantizised.y()) + if rect.height() < 0: + self.handle_selected = RectPosition.TOP + elif self.handle_selected == RectPosition.BL: + rect.setBottomLeft(pos_quantizised) + if rect.width() < 0 and rect.height() < 0: + self.handle_selected = RectPosition.TR + elif rect.width() < 0: + self.handle_selected = RectPosition.BR + elif rect.height() < 0: + self.handle_selected = RectPosition.TL + elif self.handle_selected == RectPosition.LEFT: + rect.setLeft(pos_quantizised.x()) + if rect.width() < 0: + self.handle_selected = RectPosition.RIGHT + + if rect.width() == 0 or rect.height() == 0: + self.setRect(rect) + else: + rect = rect.intersected(self.image_size) + self.setRect(rect) + self.size_changed() + super().mouseMoveEvent(event) -class ImageLabel(QLabel): - def __init__(self, scroll_area): - super().__init__() - self.scroll_area = scroll_area - self.image_path = None - self.is_zoom_to_fit = True - self.zoom_factor = 1.0 - self.in_update = False - self.setAlignment(Qt.AlignmentFlag.AlignCenter) - self.setSizePolicy(QSizePolicy.Policy.Expanding, - QSizePolicy.Policy.Expanding) - self.setScaledContents(False) - - def resizeEvent(self, event: QResizeEvent): - """Resize the image whenever the label is resized.""" - if self.image_path: - self.update_image() - - def load_image(self, image_path: Path): - self.image_path = image_path - image_reader = QImageReader(str(self.image_path)) - # Rotate the image according to the orientation tag. - image_reader.setAutoTransform(True) - self.pixmap_orig = QPixmap.fromImageReader(image_reader) - self.pixmap_orig.setDevicePixelRatio(self.devicePixelRatio()) - self.update_image() - - def update_image(self): - if not self.pixmap_orig or self.in_update: - return - - self.in_update = True - - if self.is_zoom_to_fit: - self.zoom_factor = self.zoom_fit_ratio() - - pixmap = self.pixmap_orig.scaled( - self.pixmap_orig.size() * self.pixmap_orig.devicePixelRatio() * self.zoom_factor, - Qt.AspectRatioMode.KeepAspectRatio, - Qt.TransformationMode.SmoothTransformation) - self.setPixmap(pixmap) - self.adjustSize() - self.in_update = False - - def zoom_in(self): - self.is_zoom_to_fit = False # No longer zoom to fit - self.zoom_factor = min(self.zoom_factor * 1.25, 4) - self.update_image() - - def zoom_out(self): - self.is_zoom_to_fit = False # No longer zoom to fit - zoom_fit_ratio = self.zoom_fit_ratio() - self.zoom_factor = max(self.zoom_factor / 1.25, min(self.zoom_fit_ratio(), 1.0)) - if self.zoom_factor == zoom_fit_ratio: - self.is_zoom_to_fit = True # At the limit? Activate fit mode again - self.update_image() - - def zoom_original(self): - self.is_zoom_to_fit = False # No longer zoom to fit - self.zoom_factor = 1.0 - self.update_image() - - def zoom_fit(self): - self.is_zoom_to_fit = True - self.update_image() - - def zoom_fit_ratio(self): - widget_width = self.scroll_area.viewport().width() - widget_height = self.scroll_area.viewport().height() - image_width = self.pixmap_orig.width() - image_height = self.pixmap_orig.height() - - if image_width > 0 and image_height > 0: - width_ratio = widget_width / image_width - height_ratio = widget_height / image_height - return min(width_ratio, height_ratio) + def mouseReleaseEvent(self, event): + if self.handle_selected: + self.handle_selected = None + super().mouseReleaseEvent(event) - return 1.0 # this should not happen anyway + def paint(self, painter, option, widget=None): + if self.rect_type == ImageMarking.CROP: + painter.setPen(Qt.NoPen) + painter.setBrush(QColor(255, 0, 0, 127)) + path = QPainterPath() + path.addRect(self.rect()) + to_crop = self.rect().size() - self.target_size + path.addRect(QRectF(QPointF(self.rect().x()+to_crop.width()/2, + self.rect().y()+to_crop.height()/2), self.target_size)) + painter.drawPath(path) + + pen_half_width = self.pen_half_width / self.zoom_factor + pen = QPen(self.color, 2*pen_half_width, Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin) + painter.setPen(pen) + painter.setBrush(Qt.NoBrush) + painter.drawRect(self.rect().adjusted(-pen_half_width, -pen_half_width, + pen_half_width, pen_half_width)) + + def shape(self): + path = super().shape() + adjust = (self.pen_half_width + max(self.pen_half_width, + self.handle_half_size))/self.zoom_factor + path.addRect(self.rect().adjusted(-adjust, -adjust, adjust, adjust)) + return path + + def boundingRect(self): + adjust = (self.pen_half_width + max(self.pen_half_width, + self.handle_half_size))/self.zoom_factor + return self.rect().adjusted(-adjust, -adjust, adjust, adjust) + + def size_changed(self): + if self.rect_type == ImageMarking.CROP: + bucket_strategy = settings.value('export_bucket_strategy', type=str) + current = self.rect().size() + if bucket_strategy == BucketStrategy.SCALE: + self.target_size = current + else: # CROP or CROP_SCALE + target_width, target_height = target_dimension.get(current.toTuple()) + if current.height() * target_width / current.width() < target_height: # too wide + scale = current.height() / target_height + else: # too high + scale = current.width() / target_width + self.target_size = QSize(target_width*scale, target_height*scale) + if bucket_strategy == BucketStrategy.CROP_SCALE: + self.target_size = (self.target_size + current.toSize())/2 class ImageViewer(QWidget): zoom = Signal(float, name='zoomChanged') @@ -92,149 +228,111 @@ class ImageViewer(QWidget): def __init__(self, proxy_image_list_model: ProxyImageListModel): super().__init__() self.proxy_image_list_model = proxy_image_list_model - self.drag_start_pos = None - self.drag_image_pos = None - - self.scroll_area = QScrollArea() - self.scroll_area.setFrameStyle(QFrame.NoFrame) - self.scroll_area.setWidgetResizable(True) - self.scroll_area.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded) - self.scroll_area.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded) - # Install event filter on the scroll area as the wheelEvent handler - # didn't catch everything leading to strange bugs during zooming - self.scroll_area.viewport().installEventFilter(self) - layout = QVBoxLayout(self) - layout.addWidget(self.scroll_area) + CustomRectItem.pen_half_width = round(self.devicePixelRatio()) + CustomRectItem.zoom_factor = 1.0 + self.is_zoom_to_fit = True + self.scene = QGraphicsScene() + self.view = QGraphicsView(self.scene) + self.view.setRenderHint(QPainter.Antialiasing) + self.view.setDragMode(QGraphicsView.ScrollHandDrag) + self.view.setTransformationAnchor(QGraphicsView.AnchorUnderMouse) + self.view.setResizeAnchor(QGraphicsView.AnchorUnderMouse) + settings.change.connect(self.setting_change) + + layout = QVBoxLayout() + layout.addWidget(self.view) self.setLayout(layout) + self.image_item: QGraphicsPixmapItem|None = None + self.rect_items: list[CustomRectItem] = [] - self.image_label = ImageLabel(self.scroll_area) - self.scroll_area.setWidget(self.image_label) + self.view.wheelEvent = self.wheelEvent @Slot() def load_image(self, proxy_image_index: QModelIndex): image: Image = self.proxy_image_list_model.data( proxy_image_index, Qt.ItemDataRole.UserRole) - self.image_label.load_image(image.path) - self.zoom_emit() + + pixmap = QPixmap(str(image.path)) + if self.image_item: + self.scene.removeItem(self.image_item) + self.image_item = QGraphicsPixmapItem(pixmap) + self.scene.addItem(self.image_item) + self.scene.setSceneRect(self.image_item.boundingRect() + .adjusted(-1, -1, 1, 1)) # space for rect border + CustomRectItem.image_size = self.image_item.boundingRect() + self.zoom_fit() + + image.crops = {(10,20,70,140): (64,128)} + image.hints = [(200,220,100,100), (250,270,100,200)] + for rect, target in image.crops.items(): + self.add_rectangle(QRect(*rect), ImageMarking.CROP, QSize(*target)) + for rect in image.hints: + self.add_rectangle(QRect(*rect), ImageMarking.HINT) + + @Slot() + def setting_change(self, key, value): + if key == 'export_bucket_strategy': + for rect in self.rect_items: + rect.size_changed() + self.scene.invalidate() @Slot() def zoom_in(self, center_pos: QPoint = None): - factors = self.get_scroll_area_factors() - self.image_label.zoom_in() - self.move_scroll_area(factors) + CustomRectItem.zoom_factor = min(CustomRectItem.zoom_factor * 1.25, 16) + self.is_zoom_to_fit = False self.zoom_emit() @Slot() def zoom_out(self, center_pos: QPoint = None): - factors = self.get_scroll_area_factors() - self.image_label.zoom_out() - self.move_scroll_area(factors) + view = self.view.viewport().size() + scene = self.scene.sceneRect() + CustomRectItem.zoom_factor = max(CustomRectItem.zoom_factor / 1.25, + min(view.width()/scene.width(), + view.height()/scene.height())) + self.is_zoom_to_fit = False self.zoom_emit() @Slot() def zoom_original(self): - factors = self.get_scroll_area_factors() - self.image_label.zoom_original() - self.move_scroll_area(factors) + CustomRectItem.zoom_factor = 1.0 + self.is_zoom_to_fit = False self.zoom_emit() @Slot() def zoom_fit(self): - self.image_label.zoom_fit() + self.view.fitInView(self.scene.sceneRect(), Qt.KeepAspectRatio) + CustomRectItem.zoom_factor = self.view.transform().m11() + self.is_zoom_to_fit = True self.zoom_emit() def zoom_emit(self): - if self.image_label.is_zoom_to_fit: + transform = self.view.transform() + self.view.setTransform(QTransform( + CustomRectItem.zoom_factor, transform.m12(), transform.m13(), + transform.m21(), CustomRectItem.zoom_factor, transform.m23(), + transform.m31(), transform.m32(), transform.m33())) + if self.is_zoom_to_fit: self.zoom.emit(-1) else: - self.zoom.emit(self.image_label.zoom_factor) + self.zoom.emit(CustomRectItem.zoom_factor) - def mousePressEvent(self, event: QMouseEvent): - if event.button() == Qt.MouseButton.MiddleButton: - # Reset zoom - and toggle between original size and fit mode - if self.image_label.is_zoom_to_fit: - self.zoom_original() - else: - self.zoom_fit() - elif event.button() == Qt.MouseButton.LeftButton: - self.drag_start_pos = event.pos() - self.drag_image_pos = (self.scroll_area.horizontalScrollBar().value(), self.scroll_area.verticalScrollBar().value()) - self.setCursor(QCursor(Qt.CursorShape.ClosedHandCursor)) - else: - super().mousePressEvent(event) + def wheelEvent(self, event): + old_pos = self.view.mapToScene(event.position().toPoint()) - def mouseMoveEvent(self, event: QMouseEvent): - if self.drag_start_pos: - delta = event.pos() - self.drag_start_pos - self.scroll_area.horizontalScrollBar().setValue(self.drag_image_pos[0] - delta.x()) - self.scroll_area.verticalScrollBar().setValue(self.drag_image_pos[1] - delta.y()) - super().mouseMoveEvent(event) + if event.angleDelta().y() > 0: + self.zoom_in() + else: + self.zoom_out() - def mouseReleaseEvent(self, event: QMouseEvent): - if event.button() == Qt.MouseButton.LeftButton: - self.drag_start_pos = None - self.drag_image_pos = None - self.setCursor(QCursor(Qt.CursorShape.ArrowCursor)) - super().mouseReleaseEvent(event) + new_pos = self.view.mapToScene(event.position().toPoint()) + delta = new_pos - old_pos + self.view.translate(delta.x(), delta.y()) - def eventFilter(self, source, event): - if event.type() == QEvent.Wheel: - if event.modifiers() & Qt.ControlModifier: - # Handle the control key + mouse wheel event - factors = self.get_scroll_area_factors(event.position()) - - if event.angleDelta().y() > 0: - self.zoom_in() - else: - self.zoom_out() - - self.move_scroll_area(factors) - return True # Event is handled - return super().eventFilter(source, event) - - def get_scroll_area_factors(self, position: QPointF | None = None) -> tuple[float, float, float, float]: - """ - Get the factos (fractions, percentages) of the mouse position on the - image as well as it on the scroll area. - """ - widgetPos = self.image_label.geometry() - image_size = self.image_label.pixmap_orig.size()*self.image_label.zoom_factor - if image_size.width() < self.scroll_area.viewport().width(): - offset_x = (self.scroll_area.width() - image_size.width())/2 - else: - offset_x = 0 - if image_size.height() < self.scroll_area.viewport().height(): - offset_y = (self.scroll_area.height() - image_size.height())/2 - else: - offset_y = 0 + def add_rectangle(self, rect: QRect, rect_type: ImageMarking, size: QSize = None): + rect_item = CustomRectItem(rect, rect_type, size) + self.scene.addItem(rect_item) + self.rect_items.append(rect_item) - if position: - img_fac_x = (position.x()-widgetPos.x()-offset_x)/image_size.width() - img_fac_y = (position.y()-widgetPos.y()-offset_y)/image_size.height() - scroll_area_fac_x = position.x() / self.scroll_area.viewport().width() - scroll_area_fac_y = position.y() / self.scroll_area.viewport().height() - else: - # No position -> assume center - img_fac_x = (self.scroll_area.viewport().width()/2-widgetPos.x()-offset_x)/image_size.width() - img_fac_y = (self.scroll_area.viewport().height()/2-widgetPos.y()-offset_y)/image_size.height() - scroll_area_fac_x = 0.5 - scroll_area_fac_y = 0.5 - - return (img_fac_x, img_fac_y, scroll_area_fac_x, scroll_area_fac_y) - - def move_scroll_area(self, factors): - """ - Move the image in the scroll area so that the (fractional) position - on the image appears on the (fractional) position of the scroll area - """ - img_fac_x, img_fac_y, scroll_area_fac_x, scroll_area_fac_y = factors - image_size = self.image_label.pixmap_orig.size()*self.image_label.zoom_factor - if image_size.width() > self.scroll_area.viewport().width(): - viewport_x = scroll_area_fac_x * self.scroll_area.viewport().width() - scroll_area_x = img_fac_x * image_size.width() - self.scroll_area.horizontalScrollBar().setValue(scroll_area_x - viewport_x) - if image_size.height() > self.scroll_area.viewport().height(): - viewport_y = scroll_area_fac_y * self.scroll_area.viewport().height() - scroll_area_y = img_fac_y * image_size.height() - self.scroll_area.verticalScrollBar().setValue(scroll_area_y - viewport_y) + def resizeEvent(self, event): + super().resizeEvent(event) diff --git a/taggui/widgets/main_window.py b/taggui/widgets/main_window.py index f270e296..d3465fa2 100644 --- a/taggui/widgets/main_window.py +++ b/taggui/widgets/main_window.py @@ -19,7 +19,7 @@ from utils.big_widgets import BigPushButton from utils.image import Image from utils.key_press_forwarder import KeyPressForwarder -from utils.settings import DEFAULT_SETTINGS, get_settings, get_tag_separator +from utils.settings import DEFAULT_SETTINGS, settings, get_tag_separator from utils.shortcut_remover import ShortcutRemover from utils.utils import get_resource_path, pluralize from widgets.all_tags_editor import AllTagsEditor @@ -37,11 +37,10 @@ class MainWindow(QMainWindow): def __init__(self, app: QApplication): super().__init__() self.app = app - self.settings = get_settings() # The path of the currently loaded directory. This is set later when a # directory is loaded. self.directory_path = None - image_list_image_width = self.settings.value( + image_list_image_width = settings.value( 'image_list_image_width', defaultValue=DEFAULT_SETTINGS['image_list_image_width'], type=int) tag_separator = get_tag_separator() @@ -207,13 +206,13 @@ def __init__(self, app: QApplication): def closeEvent(self, event: QCloseEvent): """Save the window geometry and state before closing.""" - self.settings.setValue('geometry', self.saveGeometry()) - self.settings.setValue('window_state', self.saveState()) + settings.setValue('geometry', self.saveGeometry()) + settings.setValue('window_state', self.saveState()) super().closeEvent(event) def set_font_size(self): font = self.app.font() - font_size = self.settings.value( + font_size = settings.value( 'font_size', defaultValue=DEFAULT_SETTINGS['font_size'], type=int) font.setPointSize(font_size) self.app.setFont(font) @@ -247,7 +246,7 @@ def load_directory(self, path: Path, select_index: int = 0, save_path_to_settings: bool = False): self.directory_path = path.resolve() if save_path_to_settings: - self.settings.setValue('directory_path', str(self.directory_path)) + settings.setValue('directory_path', str(self.directory_path)) self.setWindowTitle(path.name) self.image_list_model.load_directory(path) self.image_list.filter_line_edit.clear() @@ -282,7 +281,7 @@ def reload_directory(self): select_index_key = ('image_index' if self.proxy_image_list_model.filter is None else 'filtered_image_index') - select_index = self.settings.value(select_index_key, type=int) or 0 + select_index = settings.value(select_index_key, type=int) or 0 self.load_directory(self.directory_path) self.image_list.filter_line_edit.setText(filter_text) # If the selected image index is out of bounds due to images being @@ -459,7 +458,7 @@ def set_image_list_filter(self): self.all_tags_editor.all_tags_list.setCurrentIndex(QModelIndex()) # Select the previously selected image in the unfiltered image # list. - select_index = self.settings.value('image_index', type=int) or 0 + select_index = settings.value('image_index', type=int) or 0 self.image_list.list_view.setCurrentIndex( self.proxy_image_list_model.index(select_index, 0)) else: @@ -473,7 +472,7 @@ def save_image_index(self, proxy_image_index: QModelIndex): settings_key = ('image_index' if self.proxy_image_list_model.filter is None else 'filtered_image_index') - self.settings.setValue(settings_key, proxy_image_index.row()) + settings.setValue(settings_key, proxy_image_index.row()) def connect_toolbar_signals(self): self.zoom_fit_best_action.triggered.connect( @@ -620,19 +619,19 @@ def connect_auto_captioner_signals(self): def restore(self): # Restore the window geometry and state. - if self.settings.contains('geometry'): - self.restoreGeometry(self.settings.value('geometry', type=bytes)) + if settings.contains('geometry'): + self.restoreGeometry(settings.value('geometry', type=bytes)) else: self.showMaximized() - self.restoreState(self.settings.value('window_state', type=bytes)) + self.restoreState(settings.value('window_state', type=bytes)) # Get the last index of the last selected image. - if self.settings.contains('image_index'): - image_index = self.settings.value('image_index', type=int) + if settings.contains('image_index'): + image_index = settings.value('image_index', type=int) else: image_index = 0 # Load the last loaded directory. - if self.settings.contains('directory_path'): - directory_path = Path(self.settings.value('directory_path', + if settings.contains('directory_path'): + directory_path = Path(settings.value('directory_path', type=str)) if directory_path.is_dir(): self.load_directory(directory_path, select_index=image_index) From 4168bc08bbaae8118ba6e35cba6608587ce2cbd8 Mon Sep 17 00:00:00 2001 From: StableLlama Date: Fri, 21 Feb 2025 17:55:01 +0100 Subject: [PATCH 21/82] Show image crop in image list --- taggui/dialogs/export_dialog.py | 14 ++++---- taggui/models/image_list_model.py | 12 +++++-- taggui/models/proxy_image_list_model.py | 8 ++--- taggui/utils/image.py | 9 +++-- taggui/widgets/image_viewer.py | 46 ++++++++++++++++++------- taggui/widgets/main_window.py | 4 --- 6 files changed, 60 insertions(+), 33 deletions(-) diff --git a/taggui/dialogs/export_dialog.py b/taggui/dialogs/export_dialog.py index 736a4637..c6a84839 100644 --- a/taggui/dialogs/export_dialog.py +++ b/taggui/dialogs/export_dialog.py @@ -367,9 +367,9 @@ def show_statistics(self): image_list = self.get_image_list() image_dimensions = defaultdict(int) for this_image in image_list: - this_image.target_dimensions = target_dimension.get( + this_image.target_dimension = target_dimension.get( this_image.dimensions) - image_dimensions[this_image.target_dimensions] += 1 + image_dimensions[this_image.target_dimension] += 1 self.image_list.proxy_image_list_model.invalidate() sorted_dimensions = sorted( @@ -527,7 +527,7 @@ def do_export(self): else: image_file = image_file.convert("RGB") # Otherwise, convert to RGB - new_width, new_height = image_entry.target_dimensions + new_width, new_height = image_entry.target_dimension current_width, current_height = image_file.size if bucket_strategy == BucketStrategy.CROP or bucket_strategy == BucketStrategy.CROP_SCALE: if current_height * new_width / current_width < new_height: # too wide @@ -535,8 +535,8 @@ def do_export(self): else: # too high new_height = floor(current_height * new_width / current_width) if bucket_strategy == BucketStrategy.CROP_SCALE: - new_width = floor((image_entry.target_dimensions[0] + new_width)/2) - new_height = floor((image_entry.target_dimensions[1] + new_height)/2) + new_width = floor((image_entry.target_dimension[0] + new_width)/2) + new_height = floor((image_entry.target_dimension[1] + new_height)/2) if image_file.size[0] != new_width or image_file.size[1] != new_height: # resize with the best method available resized_image = image_file.resize((new_width, new_height), Image.LANCZOS) @@ -547,8 +547,8 @@ def do_export(self): # crop to the desired size current_width, current_height = sharpend_image.size - crop_width = floor((current_width - image_entry.target_dimensions[0]) / 2) - crop_height = floor((current_height - image_entry.target_dimensions[1]) / 2) + crop_width = floor((current_width - image_entry.target_dimension[0]) / 2) + crop_height = floor((current_height - image_entry.target_dimension[1]) / 2) cropped_image = sharpend_image.crop((crop_width, crop_height, current_width - crop_width, current_height - crop_height)) lossless = quality > 99 diff --git a/taggui/models/image_list_model.py b/taggui/models/image_list_model.py index 58992fd9..2ee1a66a 100644 --- a/taggui/models/image_list_model.py +++ b/taggui/models/image_list_model.py @@ -8,8 +8,8 @@ import exifread import imagesize -from PySide6.QtCore import (QAbstractListModel, QModelIndex, QSize, Qt, Signal, - Slot) +from PySide6.QtCore import (QAbstractListModel, QModelIndex, QRect, QSize, Qt, + Signal, Slot) from PySide6.QtGui import QIcon, QImageReader, QPixmap from PySide6.QtWidgets import QMessageBox @@ -82,11 +82,14 @@ def data(self, index, role=None) -> Image | str | QIcon | QSize: image_reader = QImageReader(str(image.path)) # Rotate the image based on the orientation tag. image_reader.setAutoTransform(True) + if image.crop: + image_reader.setClipRect(QRect(*image.crop)) pixmap = QPixmap.fromImageReader(image_reader).scaledToWidth( self.image_list_image_width, Qt.TransformationMode.SmoothTransformation) thumbnail = QIcon(pixmap) image.thumbnail = thumbnail + self.dataChanged.emit(index, index) return thumbnail if role == Qt.ItemDataRole.SizeHintRole: if image.thumbnail: @@ -156,6 +159,11 @@ def load_directory(self, directory_path: Path): tags = [tag.strip() for tag in tags] tags = [tag for tag in tags if tag] image = Image(image_path, dimensions, tags) + ### TEMP FIXME start + image.crop = (10,20,170,240) + image.target_dimension = (64,128) + image.hints = [(200,220,100,100), (250,270,100,200)] + ### TEMP FIXME end self.images.append(image) self.images.sort(key=lambda image_: image_.path) self.modelReset.emit() diff --git a/taggui/models/proxy_image_list_model.py b/taggui/models/proxy_image_list_model.py index 45d6fcbe..25e050c9 100644 --- a/taggui/models/proxy_image_list_model.py +++ b/taggui/models/proxy_image_list_model.py @@ -46,11 +46,11 @@ def does_image_match_filter(self, image: Image, if filter_[0] == 'target': # accept any dimension separator of [x:] dimension = (filter_[1]).replace(':', 'x').split('x') - if image.target_dimensions == None: - image.target_dimensions = target_dimension.get(image.dimensions) + if image.target_dimension == None: + image.target_dimension = target_dimension.get(image.dimensions) return (len(dimension) == 2 - and dimension[0] == str(image.target_dimensions[0]) - and dimension[1] == str(image.target_dimensions[1])) + and dimension[0] == str(image.target_dimension[0]) + and dimension[1] == str(image.target_dimension[1])) if filter_[1] == 'AND': return (self.does_image_match_filter(image, filter_[0]) and self.does_image_match_filter(image, filter_[2:])) diff --git a/taggui/utils/image.py b/taggui/utils/image.py index e01991ad..d2f251ed 100644 --- a/taggui/utils/image.py +++ b/taggui/utils/image.py @@ -9,7 +9,10 @@ class Image: path: Path dimensions: tuple[int, int] | None tags: list[str] = field(default_factory=list) - target_dimensions: tuple[int, int] | None = None - # store for each crop (x, y, width, height) the target dimension (width, heiht) - crops: dict[tuple[int, int, int, int], tuple[int, int]] | None = None + target_dimension: tuple[int, int] | None = None + # (x, y, width, height) + crop: tuple[int, int, int, int] | None = None + hints: list[tuple[int, int, int, int]] = field(default_factory=list) + includes: list[tuple[int, int, int, int]] = field(default_factory=list) + excludes: list[tuple[int, int, int, int]] = field(default_factory=list) thumbnail: QIcon | None = None diff --git a/taggui/widgets/image_viewer.py b/taggui/widgets/image_viewer.py index ca9ac12b..c39d22bc 100644 --- a/taggui/widgets/image_viewer.py +++ b/taggui/widgets/image_viewer.py @@ -1,8 +1,8 @@ from enum import Enum from math import floor from pathlib import Path -from PySide6.QtCore import (QModelIndex, QPoint, QPointF, QRect, QRectF, QSize, - QSizeF, Qt, Signal, Slot, QEvent) +from PySide6.QtCore import (QEvent, QModelIndex, QObject, QPoint, QPointF, + QRect, QRectF, QSize, QSizeF, Qt, Signal, Slot) from PySide6.QtGui import (QCursor, QColor, QPainter, QPainterPath, QPen, QPixmap, QTransform) from PySide6.QtWidgets import (QGraphicsItem, QGraphicsPixmapItem, @@ -30,6 +30,9 @@ class RectPosition(str, Enum): BL = 'bottom left' LEFT = 'left' +class RectItemSignal(QObject): + change = Signal(QGraphicsRectItem, name='markingChanged') + class CustomRectItem(QGraphicsRectItem): # the halfed size of the pen in local coordinates to make sure it stays the # same during zooming @@ -45,6 +48,7 @@ def __init__(self, rect: QRect, rect_type: ImageMarking, target_size: QSize=None self.setFlag(QGraphicsItem.ItemIsSelectable, True) self.setFlag(QGraphicsItem.ItemSendsScenePositionChanges, True) self.setAcceptHoverEvents(True) + self.signal = RectItemSignal() self.rect_type = rect_type self.color = { ImageMarking.CROP: Qt.blue, @@ -174,6 +178,7 @@ def mouseMoveEvent(self, event): def mouseReleaseEvent(self, event): if self.handle_selected: self.handle_selected = None + self.signal.change.emit(self) super().mouseReleaseEvent(event) def paint(self, painter, option, widget=None): @@ -243,32 +248,34 @@ def __init__(self, proxy_image_list_model: ProxyImageListModel): layout.addWidget(self.view) self.setLayout(layout) - self.image_item: QGraphicsPixmapItem|None = None + self.proxy_image_index = None self.rect_items: list[CustomRectItem] = [] self.view.wheelEvent = self.wheelEvent @Slot() def load_image(self, proxy_image_index: QModelIndex): + self.proxy_image_index = proxy_image_index image: Image = self.proxy_image_list_model.data( proxy_image_index, Qt.ItemDataRole.UserRole) pixmap = QPixmap(str(image.path)) - if self.image_item: - self.scene.removeItem(self.image_item) - self.image_item = QGraphicsPixmapItem(pixmap) - self.scene.addItem(self.image_item) - self.scene.setSceneRect(self.image_item.boundingRect() + self.scene.clear() + image_item = QGraphicsPixmapItem(pixmap) + self.scene.addItem(image_item) + self.scene.setSceneRect(image_item.boundingRect() .adjusted(-1, -1, 1, 1)) # space for rect border - CustomRectItem.image_size = self.image_item.boundingRect() + CustomRectItem.image_size = image_item.boundingRect() self.zoom_fit() - image.crops = {(10,20,70,140): (64,128)} - image.hints = [(200,220,100,100), (250,270,100,200)] - for rect, target in image.crops.items(): - self.add_rectangle(QRect(*rect), ImageMarking.CROP, QSize(*target)) + if image.crop: + self.add_rectangle(QRect(*image.crop), ImageMarking.CROP, QSize(*image.target_dimension)) for rect in image.hints: self.add_rectangle(QRect(*rect), ImageMarking.HINT) + for rect in image.includes: + self.add_rectangle(QRect(*rect), ImageMarking.INCLUDE) + for rect in image.excludes: + self.add_rectangle(QRect(*rect), ImageMarking.EXCLUDE) @Slot() def setting_change(self, key, value): @@ -277,6 +284,18 @@ def setting_change(self, key, value): rect.size_changed() self.scene.invalidate() + @Slot(QGraphicsRectItem) + def marking_change(self, rect: QGraphicsRectItem): + assert self.proxy_image_index != None + if rect.rect_type == ImageMarking.CROP: + image: Image = self.proxy_image_list_model.data( + self.proxy_image_index, Qt.ItemDataRole.UserRole) + image.thumbnail = None + image.crop = rect.rect().getRect() + image.target_dimension = rect.target_size.toTuple() + self.proxy_image_list_model.dataChanged.emit(self.proxy_image_index, + self.proxy_image_index) + @Slot() def zoom_in(self, center_pos: QPoint = None): CustomRectItem.zoom_factor = min(CustomRectItem.zoom_factor * 1.25, 16) @@ -331,6 +350,7 @@ def wheelEvent(self, event): def add_rectangle(self, rect: QRect, rect_type: ImageMarking, size: QSize = None): rect_item = CustomRectItem(rect, rect_type, size) + rect_item.signal.change.connect(self.marking_change) self.scene.addItem(rect_item) self.rect_items.append(rect_item) diff --git a/taggui/widgets/main_window.py b/taggui/widgets/main_window.py index d3465fa2..167d55c5 100644 --- a/taggui/widgets/main_window.py +++ b/taggui/widgets/main_window.py @@ -71,17 +71,13 @@ def __init__(self, app: QApplication): self.addToolBar(self.toolbar) self.zoom_fit_best_action = QAction(QIcon.fromTheme('zoom-fit-best'), 'Zoom to fit', self) self.zoom_fit_best_action.setCheckable(True) - #self.zoom_fit_best_action.triggered.connect(self.image_viewer.zoom_fit) self.toolbar.addAction(self.zoom_fit_best_action) self.zoom_in_action = QAction(QIcon.fromTheme('zoom-in'), 'Zoom in', self) - #self.zoom_in_action.triggered.connect(self.image_viewer.zoom_in) self.toolbar.addAction(self.zoom_in_action) self.zoom_original_action = QAction(QIcon.fromTheme('zoom-original'), 'Original size', self) self.zoom_original_action.setCheckable(True) - #self.zoom_original_action.triggered.connect(self.image_viewer.zoom_original) self.toolbar.addAction(self.zoom_original_action) self.zoom_out_action = QAction(QIcon.fromTheme('zoom-out'), 'Zoom out', self) - #self.zoom_out_action.triggered.connect(self.image_viewer.zoom_out) self.toolbar.addAction(self.zoom_out_action) self.toolbar.addSeparator() From a6afbf59f5ab1c34c428b20aad771e01a4fa143e Mon Sep 17 00:00:00 2001 From: StableLlama Date: Fri, 21 Feb 2025 22:52:02 +0100 Subject: [PATCH 22/82] Simplify code --- taggui/widgets/image_viewer.py | 54 ++++++++++++++-------------------- 1 file changed, 22 insertions(+), 32 deletions(-) diff --git a/taggui/widgets/image_viewer.py b/taggui/widgets/image_viewer.py index c39d22bc..b4d0efd6 100644 --- a/taggui/widgets/image_viewer.py +++ b/taggui/widgets/image_viewer.py @@ -30,6 +30,27 @@ class RectPosition(str, Enum): BL = 'bottom left' LEFT = 'left' +def flip_rect_position(pos: RectPosition, h_flip: bool, v_flip: bool) -> RectPosition: + if pos == RectPosition.TL or pos == RectPosition.TOP or pos == RectPosition.TR: + v = 2 if v_flip else 0 + elif pos == RectPosition.LEFT or pos == RectPosition.RIGHT: + v = 1 + else: + v = 0 if v_flip else 2 + + if pos == RectPosition.TL or pos == RectPosition.LEFT or pos == RectPosition.BL: + h = 2 if h_flip else 0 + elif pos == RectPosition.TOP or pos == RectPosition.BOTTOM: + h = 1 + else: + h = 0 if h_flip else 2 + + return { + 0: RectPosition.TL, 1: RectPosition.TOP, 2: RectPosition.TR, + 10: RectPosition.LEFT, 12: RectPosition.RIGHT, + 20: RectPosition.BL, 21: RectPosition.BOTTOM, 22: RectPosition.BR, + }[h+10*v] + class RectItemSignal(QObject): change = Signal(QGraphicsRectItem, name='markingChanged') @@ -120,52 +141,21 @@ def mouseMoveEvent(self, event): pos_quantizised = event.scenePos().toPoint().toPointF() if self.handle_selected == RectPosition.TL: rect.setTopLeft(pos_quantizised) - if rect.width() < 0 and rect.height() < 0: - self.handle_selected = RectPosition.BR - elif rect.width() < 0: - self.handle_selected = RectPosition.TR - elif rect.height() < 0: - self.handle_selected = RectPosition.BL elif self.handle_selected == RectPosition.TOP: rect.setTop(pos_quantizised.y()) - if rect.height() < 0: - self.handle_selected = RectPosition.BOTTOM elif self.handle_selected == RectPosition.TR: rect.setTopRight(pos_quantizised) - if rect.width() < 0 and rect.height() < 0: - self.handle_selected = RectPosition.BL - elif rect.width() < 0: - self.handle_selected = RectPosition.TL - elif rect.height() < 0: - self.handle_selected = RectPosition.BR elif self.handle_selected == RectPosition.RIGHT: rect.setRight(pos_quantizised.x()) - if rect.width() < 0: - self.handle_selected = RectPosition.LEFT elif self.handle_selected == RectPosition.BR: rect.setBottomRight(pos_quantizised) - if rect.width() < 0 and rect.height() < 0: - self.handle_selected = RectPosition.TL - elif rect.width() < 0: - self.handle_selected = RectPosition.BL - elif rect.height() < 0: - self.handle_selected = RectPosition.TR elif self.handle_selected == RectPosition.BOTTOM: rect.setBottom(pos_quantizised.y()) - if rect.height() < 0: - self.handle_selected = RectPosition.TOP elif self.handle_selected == RectPosition.BL: rect.setBottomLeft(pos_quantizised) - if rect.width() < 0 and rect.height() < 0: - self.handle_selected = RectPosition.TR - elif rect.width() < 0: - self.handle_selected = RectPosition.BR - elif rect.height() < 0: - self.handle_selected = RectPosition.TL elif self.handle_selected == RectPosition.LEFT: rect.setLeft(pos_quantizised.x()) - if rect.width() < 0: - self.handle_selected = RectPosition.RIGHT + self.handle_selected = flip_rect_position(self.handle_selected, rect.width() < 0, rect.height() < 0) if rect.width() == 0 or rect.height() == 0: self.setRect(rect) From d8eb59529adcac8d2e30b500f2158f37ffaa82db Mon Sep 17 00:00:00 2001 From: StableLlama Date: Sat, 22 Feb 2025 15:59:23 +0100 Subject: [PATCH 23/82] Add head up display to show hint about preferred aspect ratios --- taggui/utils/target_dimension.py | 11 ++- taggui/widgets/image_viewer.py | 136 +++++++++++++++++++++++++++---- 2 files changed, 131 insertions(+), 16 deletions(-) diff --git a/taggui/utils/target_dimension.py b/taggui/utils/target_dimension.py index ca64a5f8..f253ae7c 100644 --- a/taggui/utils/target_dimension.py +++ b/taggui/utils/target_dimension.py @@ -4,7 +4,16 @@ from utils.settings import DEFAULT_SETTINGS, settings # singleton data store -_preferred_sizes : list[tuple[int, int]] | None = None +_preferred_sizes : list[tuple[int, int]] = [] + +settings.change.connect(lambda: _preferred_sizes.clear()) + +def get_preferred_sizes(): + global _preferred_sizes + if not _preferred_sizes: + prepare() + return _preferred_sizes + def prepare(aspect_ratios : list[tuple[int, int, int]] | None = None) -> list[tuple[int, int, int]] | None: """ diff --git a/taggui/widgets/image_viewer.py b/taggui/widgets/image_viewer.py index b4d0efd6..9fc1cb96 100644 --- a/taggui/widgets/image_viewer.py +++ b/taggui/widgets/image_viewer.py @@ -1,8 +1,9 @@ from enum import Enum from math import floor from pathlib import Path -from PySide6.QtCore import (QEvent, QModelIndex, QObject, QPoint, QPointF, - QRect, QRectF, QSize, QSizeF, Qt, Signal, Slot) +from PySide6.QtCore import (QEvent, QModelIndex, QObject, QPersistentModelIndex, + QPoint, QPointF, QRect, QRectF, QSize, QSizeF, Qt, + Signal, Slot) from PySide6.QtGui import (QCursor, QColor, QPainter, QPainterPath, QPen, QPixmap, QTransform) from PySide6.QtWidgets import (QGraphicsItem, QGraphicsPixmapItem, @@ -29,8 +30,12 @@ class RectPosition(str, Enum): BOTTOM = 'bottom' BL = 'bottom left' LEFT = 'left' + NONE = 'none' def flip_rect_position(pos: RectPosition, h_flip: bool, v_flip: bool) -> RectPosition: + if pos == RectPosition.NONE: + return RectPosition.NONE + if pos == RectPosition.TL or pos == RectPosition.TOP or pos == RectPosition.TR: v = 2 if v_flip else 0 elif pos == RectPosition.LEFT or pos == RectPosition.RIGHT: @@ -46,13 +51,14 @@ def flip_rect_position(pos: RectPosition, h_flip: bool, v_flip: bool) -> RectPos h = 0 if h_flip else 2 return { - 0: RectPosition.TL, 1: RectPosition.TOP, 2: RectPosition.TR, + 0: RectPosition.TL, 1: RectPosition.TOP, 2: RectPosition.TR, 10: RectPosition.LEFT, 12: RectPosition.RIGHT, 20: RectPosition.BL, 21: RectPosition.BOTTOM, 22: RectPosition.BR, }[h+10*v] class RectItemSignal(QObject): - change = Signal(QGraphicsRectItem, name='markingChanged') + change = Signal(QGraphicsRectItem, name='rectChanged') + move = Signal(QRectF, RectPosition, name='rectIsMoving') class CustomRectItem(QGraphicsRectItem): # the halfed size of the pen in local coordinates to make sure it stays the @@ -64,7 +70,8 @@ class CustomRectItem(QGraphicsRectItem): # The size of the image this rect belongs to image_size = QRectF(0, 0, 1, 1) - def __init__(self, rect: QRect, rect_type: ImageMarking, target_size: QSize=None, parent=None): + def __init__(self, rect: QRect, rect_type: ImageMarking, + target_size: QSize | None = None, parent = None): super().__init__(rect.toRectF(), parent) self.setFlag(QGraphicsItem.ItemIsSelectable, True) self.setFlag(QGraphicsItem.ItemSendsScenePositionChanges, True) @@ -85,7 +92,7 @@ def __init__(self, rect: QRect, rect_type: ImageMarking, target_size: QSize=None def change_pen_half_width(self): self.prepareGeometryChange() - def handleAt(self, point: QPointF) -> RectPosition | None: + def handleAt(self, point: QPointF) -> RectPosition: handle_space = -min(self.pen_half_width - self.handle_half_size, 0)/self.zoom_factor left = point.x() < self.rect().left() + handle_space right = point.x() > self.rect().right() - handle_space @@ -108,7 +115,7 @@ def handleAt(self, point: QPointF) -> RectPosition | None: elif right: return RectPosition.RIGHT - return None + return RectPosition.NONE def hoverMoveEvent(self, event): handle = self.handleAt(event.pos()) @@ -131,6 +138,7 @@ def mousePressEvent(self, event): self.mouse_press_pos = event.pos() self.mouse_press_scene_pos = event.scenePos() self.mouse_press_rect = self.rect() + self.signal.move.emit(self.rect(), self.handle_selected) else: event.ignore() super().mousePressEvent(event) @@ -163,11 +171,14 @@ def mouseMoveEvent(self, event): rect = rect.intersected(self.image_size) self.setRect(rect) self.size_changed() + + self.signal.move.emit(self.rect(), self.handle_selected) super().mouseMoveEvent(event) def mouseReleaseEvent(self, event): if self.handle_selected: - self.handle_selected = None + self.handle_selected = RectPosition.NONE + self.signal.move.emit(self.rect(), self.handle_selected) self.signal.change.emit(self) super().mouseReleaseEvent(event) @@ -217,6 +228,93 @@ def size_changed(self): if bucket_strategy == BucketStrategy.CROP_SCALE: self.target_size = (self.target_size + current.toSize())/2 +class ResizeHintHUD(QGraphicsItem): + zoom_factor = 1.0 + + def __init__(self, boundingRect: QRectF, parent=None): + super().__init__(parent) + self._boundingRect = boundingRect + self.rect = QRectF(0, 0, 1, 1) + self.path = QPainterPath() + + @Slot(QRectF, RectPosition) + def setValues(self, rect: QRectF, pos: RectPosition): + if self.rect == rect and self.isVisible() == (pos != RectPosition.NONE): + return + + self.rect = rect + self.setVisible(pos != RectPosition.NONE) + + self.path = QPainterPath() + + if pos == RectPosition.TL: + self.add_hyperbola_limit(self.rect.bottomRight(), -1, -1) + elif pos == RectPosition.TOP: + self.add_line_limit_lr(self.rect.bottom(), -1) + elif pos == RectPosition.TR: + self.add_hyperbola_limit(self.rect.bottomLeft(), 1, -1) + elif pos == RectPosition.RIGHT: + self.add_line_limit_td(self.rect.x(), 1) + elif pos == RectPosition.BR: + self.add_hyperbola_limit(self.rect.topLeft(), 1, 1) + elif pos == RectPosition.BOTTOM: + self.add_line_limit_lr(self.rect.y(), 1) + elif pos == RectPosition.BL: + self.add_hyperbola_limit(self.rect.topRight(), -1, 1) + elif pos == RectPosition.LEFT: + self.add_line_limit_td(self.rect.right(), -1) + + self.update() + + def add_line_limit_td(self, x: int, lr: int): + width = settings.value('export_resolution', type=int)**2 / self.rect.height() + self.path.moveTo(x + lr * width, self.rect.y() ) + self.path.lineTo(x + lr * width, self.rect.y() + self.rect.height()) + + for ar in target_dimension.get_preferred_sizes(): + self.path.moveTo(x + lr * ar[0] , self.rect.y() + ar[1] ) + self.path.lineTo(x + lr * ar[0] * 2, self.rect.y() + ar[1] * 2) + self.path.moveTo(x + lr * ar[0] , self.rect.bottom() - ar[1] ) + self.path.lineTo(x + lr * ar[0] * 2, self.rect.bottom() - ar[1] * 2) + + def add_line_limit_lr(self, y: int, td: int): + height = settings.value('export_resolution', type=int)**2 / self.rect.width() + self.path.moveTo(self.rect.x(), y + td * height) + self.path.lineTo(self.rect.x() + self.rect.width(), y + td * height) + + for ar in target_dimension.get_preferred_sizes(): + self.path.moveTo(self.rect.x() + ar[0] , y + td * ar[1] ) + self.path.lineTo(self.rect.x() + ar[0] * 2, y + td * ar[1] * 2) + self.path.moveTo(self.rect.right() - ar[0] , y + td * ar[1] ) + self.path.lineTo(self.rect.right() - ar[0] * 2, y + td * ar[1] * 2) + + def add_hyperbola_limit(self, pos: QPoint, lr: int, td: int): + target_area = settings.value('export_resolution', type=int)**2 + res_size = settings.value('export_bucket_res_size', type=int) + dx = res_size + self.path.moveTo(pos.x() + lr * dx, pos.y() + td * target_area / dx) + while dx * res_size <= target_area: + self.path.lineTo(pos.x() + lr * dx, pos.y() + td * target_area / dx) + dx = dx + 10 + + for ar in target_dimension.get_preferred_sizes(): + self.path.moveTo(pos.x()+lr * ar[0], pos.y()+td * ar[1]) + self.path.lineTo(pos.x()+lr * 2*ar[0], pos.y()+td * 2*ar[1]) + + def boundingRect(self): + return self._boundingRect + + def paint(self, painter, option, widget=None): + clip_path = QPainterPath() + clip_path.addRect(self._boundingRect) + painter.setClipPath(clip_path) + pen = QPen(QColor(255, 255, 255, 127), 3/self.zoom_factor) + painter.setPen(pen) + painter.drawPath(self.path) + pen = QPen(QColor(0, 0, 0), 1/self.zoom_factor) + painter.setPen(pen) + painter.drawPath(self.path) + class ImageViewer(QWidget): zoom = Signal(float, name='zoomChanged') @@ -238,19 +336,21 @@ def __init__(self, proxy_image_list_model: ProxyImageListModel): layout.addWidget(self.view) self.setLayout(layout) - self.proxy_image_index = None + self.proxy_image_index: QPersistentModelIndex = None self.rect_items: list[CustomRectItem] = [] self.view.wheelEvent = self.wheelEvent @Slot() def load_image(self, proxy_image_index: QModelIndex): - self.proxy_image_index = proxy_image_index - image: Image = self.proxy_image_list_model.data( - proxy_image_index, Qt.ItemDataRole.UserRole) + self.proxy_image_index = QPersistentModelIndex(proxy_image_index) - pixmap = QPixmap(str(image.path)) self.scene.clear() + if not self.proxy_image_index.isValid(): + return + + image: Image = self.proxy_image_index.data(Qt.ItemDataRole.UserRole) + pixmap = QPixmap(str(image.path)) image_item = QGraphicsPixmapItem(pixmap) self.scene.addItem(image_item) self.scene.setSceneRect(image_item.boundingRect() @@ -258,6 +358,9 @@ def load_image(self, proxy_image_index: QModelIndex): CustomRectItem.image_size = image_item.boundingRect() self.zoom_fit() + self.hud_item = ResizeHintHUD(CustomRectItem.image_size) + self.scene.addItem(self.hud_item) + if image.crop: self.add_rectangle(QRect(*image.crop), ImageMarking.CROP, QSize(*image.target_dimension)) for rect in image.hints: @@ -277,9 +380,9 @@ def setting_change(self, key, value): @Slot(QGraphicsRectItem) def marking_change(self, rect: QGraphicsRectItem): assert self.proxy_image_index != None + assert self.proxy_image_index.isValid() if rect.rect_type == ImageMarking.CROP: - image: Image = self.proxy_image_list_model.data( - self.proxy_image_index, Qt.ItemDataRole.UserRole) + image: Image = self.proxy_image_index.data(Qt.ItemDataRole.UserRole) image.thumbnail = None image.crop = rect.rect().getRect() image.target_dimension = rect.target_size.toTuple() @@ -316,6 +419,7 @@ def zoom_fit(self): self.zoom_emit() def zoom_emit(self): + ResizeHintHUD.zoom_factor = CustomRectItem.zoom_factor transform = self.view.transform() self.view.setTransform(QTransform( CustomRectItem.zoom_factor, transform.m12(), transform.m13(), @@ -340,6 +444,8 @@ def wheelEvent(self, event): def add_rectangle(self, rect: QRect, rect_type: ImageMarking, size: QSize = None): rect_item = CustomRectItem(rect, rect_type, size) + if rect_type == ImageMarking.CROP: + rect_item.signal.move.connect(self.hud_item.setValues) rect_item.signal.change.connect(self.marking_change) self.scene.addItem(rect_item) self.rect_items.append(rect_item) From 87517d05ec172f2f124efec6705f081f9772a379 Mon Sep 17 00:00:00 2001 From: StableLlama Date: Sat, 22 Feb 2025 16:17:17 +0100 Subject: [PATCH 24/82] Little fixes --- taggui/widgets/image_viewer.py | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/taggui/widgets/image_viewer.py b/taggui/widgets/image_viewer.py index 9fc1cb96..92dbae89 100644 --- a/taggui/widgets/image_viewer.py +++ b/taggui/widgets/image_viewer.py @@ -128,13 +128,13 @@ def hoverMoveEvent(self, event): elif handle == RectPosition.LEFT or handle == RectPosition.RIGHT: self.setCursor(Qt.SizeHorCursor) else: - self.setCursor(Qt.OpenHandCursor) + self.unsetCursor() event.ignore() super().hoverMoveEvent(event) def mousePressEvent(self, event): self.handle_selected = self.handleAt(event.pos()) - if self.handle_selected: + if self.handle_selected != RectPosition.NONE: self.mouse_press_pos = event.pos() self.mouse_press_scene_pos = event.scenePos() self.mouse_press_rect = self.rect() @@ -189,8 +189,8 @@ def paint(self, painter, option, widget=None): path = QPainterPath() path.addRect(self.rect()) to_crop = self.rect().size() - self.target_size - path.addRect(QRectF(QPointF(self.rect().x()+to_crop.width()/2, - self.rect().y()+to_crop.height()/2), self.target_size)) + path.addRect(QRectF(QPointF(self.rect().x()+floor(to_crop.width()/2), + self.rect().y()+floor(to_crop.height()/2)), self.target_size)) painter.drawPath(path) pen_half_width = self.pen_half_width / self.zoom_factor @@ -272,10 +272,12 @@ def add_line_limit_td(self, x: int, lr: int): self.path.lineTo(x + lr * width, self.rect.y() + self.rect.height()) for ar in target_dimension.get_preferred_sizes(): + f = max(self._boundingRect.width() / ar[0], + self._boundingRect.height() / ar[1], 2) self.path.moveTo(x + lr * ar[0] , self.rect.y() + ar[1] ) - self.path.lineTo(x + lr * ar[0] * 2, self.rect.y() + ar[1] * 2) + self.path.lineTo(x + lr * ar[0] * f, self.rect.y() + ar[1] * f) self.path.moveTo(x + lr * ar[0] , self.rect.bottom() - ar[1] ) - self.path.lineTo(x + lr * ar[0] * 2, self.rect.bottom() - ar[1] * 2) + self.path.lineTo(x + lr * ar[0] * f, self.rect.bottom() - ar[1] * f) def add_line_limit_lr(self, y: int, td: int): height = settings.value('export_resolution', type=int)**2 / self.rect.width() @@ -283,10 +285,12 @@ def add_line_limit_lr(self, y: int, td: int): self.path.lineTo(self.rect.x() + self.rect.width(), y + td * height) for ar in target_dimension.get_preferred_sizes(): + f = max(self._boundingRect.width() / ar[0], + self._boundingRect.height() / ar[1], 2) self.path.moveTo(self.rect.x() + ar[0] , y + td * ar[1] ) - self.path.lineTo(self.rect.x() + ar[0] * 2, y + td * ar[1] * 2) + self.path.lineTo(self.rect.x() + ar[0] * f, y + td * ar[1] * f) self.path.moveTo(self.rect.right() - ar[0] , y + td * ar[1] ) - self.path.lineTo(self.rect.right() - ar[0] * 2, y + td * ar[1] * 2) + self.path.lineTo(self.rect.right() - ar[0] * f, y + td * ar[1] * f) def add_hyperbola_limit(self, pos: QPoint, lr: int, td: int): target_area = settings.value('export_resolution', type=int)**2 @@ -298,8 +302,10 @@ def add_hyperbola_limit(self, pos: QPoint, lr: int, td: int): dx = dx + 10 for ar in target_dimension.get_preferred_sizes(): - self.path.moveTo(pos.x()+lr * ar[0], pos.y()+td * ar[1]) - self.path.lineTo(pos.x()+lr * 2*ar[0], pos.y()+td * 2*ar[1]) + f = max(self._boundingRect.width() / ar[0], + self._boundingRect.height() / ar[1], 2) + self.path.moveTo(pos.x() + lr * ar[0] , pos.y() + td * ar[1] ) + self.path.lineTo(pos.x() + lr * ar[0] * f, pos.y() + td * ar[1] * f) def boundingRect(self): return self._boundingRect @@ -308,10 +314,10 @@ def paint(self, painter, option, widget=None): clip_path = QPainterPath() clip_path.addRect(self._boundingRect) painter.setClipPath(clip_path) - pen = QPen(QColor(255, 255, 255, 127), 3/self.zoom_factor) + pen = QPen(QColor(255, 255, 255, 127), 3 / self.zoom_factor) painter.setPen(pen) painter.drawPath(self.path) - pen = QPen(QColor(0, 0, 0), 1/self.zoom_factor) + pen = QPen(QColor(0, 0, 0), 1 / self.zoom_factor) painter.setPen(pen) painter.drawPath(self.path) From 43d20c7f48983a9e12c1df14f352913ef5e81e65 Mon Sep 17 00:00:00 2001 From: StableLlama Date: Sat, 22 Feb 2025 23:47:40 +0100 Subject: [PATCH 25/82] Store meta changes to disk --- taggui/models/image_list_model.py | 101 +++++++++++++++++++++++++++--- taggui/run_gui.py | 2 +- taggui/utils/image.py | 6 +- taggui/widgets/image_viewer.py | 39 ++++++++---- 4 files changed, 122 insertions(+), 26 deletions(-) diff --git a/taggui/models/image_list_model.py b/taggui/models/image_list_model.py index 2ee1a66a..621cea2f 100644 --- a/taggui/models/image_list_model.py +++ b/taggui/models/image_list_model.py @@ -5,6 +5,7 @@ from dataclasses import dataclass from enum import Enum from pathlib import Path +import json import exifread import imagesize @@ -59,6 +60,11 @@ def __init__(self, image_list_image_width: int, tag_separator: str): self.redo_stack = [] self.proxy_image_list_model = None self.image_list_selection_model = None + self.dataChanged.connect(lambda start, end: [ + self.write_meta_to_disk( + start.sibling(row, start.column()) + .data(Qt.ItemDataRole.UserRole)) + for row in range(start.row(), end.row()+1)]) def rowCount(self, parent=None) -> int: return len(self.images) @@ -108,6 +114,7 @@ def load_directory(self, directory_path: Path): self.undo_stack.clear() self.redo_stack.clear() self.update_undo_and_redo_actions_requested.emit() + error_messages: list[str] = [] file_paths = get_file_paths(directory_path) image_suffixes_string = settings.value( 'image_list_file_formats', @@ -124,6 +131,8 @@ def load_directory(self, directory_path: Path): # strings. text_file_path_strings = {str(path) for path in file_paths if path.suffix == '.txt'} + json_file_path_strings = {str(path) for path in file_paths + if path.suffix == '.json'} for image_path in image_paths: try: dimensions = imagesize.get(image_path) @@ -141,11 +150,11 @@ def load_directory(self, directory_path: Path): for value in (5, 6, 7, 8)): dimensions = (dimensions[1], dimensions[0]) except Exception as exception: - print(f'Failed to get Exif tags for {image_path}: ' - f'{exception}', file=sys.stderr) + error_messages.append(f'Failed to get Exif tags for ' + f'{image_path}: {exception}') except (ValueError, OSError) as exception: - print(f'Failed to get dimensions for {image_path}: ' - f'{exception}', file=sys.stderr) + error_messages.append(f'Failed to get dimensions for ' + f'{image_path}: {exception}') dimensions = None tags = [] text_file_path = image_path.with_suffix('.txt') @@ -159,14 +168,64 @@ def load_directory(self, directory_path: Path): tags = [tag.strip() for tag in tags] tags = [tag for tag in tags if tag] image = Image(image_path, dimensions, tags) - ### TEMP FIXME start - image.crop = (10,20,170,240) - image.target_dimension = (64,128) - image.hints = [(200,220,100,100), (250,270,100,200)] - ### TEMP FIXME end + json_file_path = image_path.with_suffix('.json') + if (str(json_file_path) in json_file_path_strings and + json_file_path.stat().st_size > 0): + with json_file_path.open(encoding="UTF-8") as source: + try: + meta = json.load(source) + except json.JSONDecodeError as e: + error_messages.append(f'Invalid JSON in ' + f'"{json_file_path}": {e.msg}') + break + except UnicodeDecodeError as e: + error_messages.append(f'Invalid Unicode in JSON in ' + f'"{json_file_path}": {e.reason}') + break + + if meta.get('version') == 1: + crop = meta.get('crop') + if crop and type(crop) is list and len(crop) == 4: + image.crop = tuple(crop) + target_dimension = meta.get('target_dimension') + if (target_dimension and type(target_dimension) is list + and len(target_dimension) == 2): + image.target_dimension = tuple(target_dimension) + hints = meta.get('hints') + if hints and type(hints) is dict: + image.hints = {} + for key, value in hints.items(): + if (value and type(value) is list and + len(value) == 4): + image.hints[key] = tuple(value) + includes = meta.get('includes') + if includes and type(includes) is dict: + image.includes = {} + for key, value in includes.items(): + if (value and type(value) is list and + len(value) == 4): + image.includes[key] = tuple(value) + excludes = meta.get('excludes') + if excludes and type(excludes) is dict: + image.excludes = {} + for key, value in excludes.items(): + if (value and type(value) is list and + len(value) == 4): + image.excludes[key] = tuple(value) + else: + error_messages.append(f'Invalid version ' + f'"{meta.get('version')}" in ' + f'"{json_file_path}"') self.images.append(image) self.images.sort(key=lambda image_: image_.path) self.modelReset.emit() + if len(error_messages) > 0: + print('\n'.join(error_messages), file=sys.stderr) + error_message_box = QMessageBox() + error_message_box.setWindowTitle('Directory reading error') + error_message_box.setIcon(QMessageBox.Icon.Warning) + error_message_box.setText('\n'.join(error_messages)) + error_message_box.exec() def add_to_undo_stack(self, action_name: str, should_ask_for_confirmation: bool): @@ -189,6 +248,30 @@ def write_image_tags_to_disk(self, image: Image): error_message_box.setText(f'Failed to save tags for {image.path}.') error_message_box.exec() + def write_meta_to_disk(self, image: Image): + does_exist = image.path.with_suffix('.json').exists() + meta = {'version': 1} + if image.crop: + meta['crop'] = image.crop + if image.target_dimension: + meta['target_dimension'] = image.target_dimension + if image.hints: + meta['hints'] = image.hints + if image.includes: + meta['includes'] = image.includes + if image.excludes: + meta['excludes'] = image.excludes + if does_exist or len(meta.keys()) > 1: + try: + with image.path.with_suffix('.json').open('w', encoding='UTF-8') as meta_file: + json.dump(meta, meta_file) + except OSError: + error_message_box = QMessageBox() + error_message_box.setWindowTitle('Error') + error_message_box.setIcon(QMessageBox.Icon.Critical) + error_message_box.setText(f'Failed to save JSON for {image.path}.') + error_message_box.exec() + def restore_history_tags(self, is_undo: bool): if is_undo: source_stack = self.undo_stack diff --git a/taggui/run_gui.py b/taggui/run_gui.py index 5f19d722..68ecece9 100644 --- a/taggui/run_gui.py +++ b/taggui/run_gui.py @@ -49,7 +49,7 @@ def run_gui(): try: run_gui() except Exception as exception: - settings.clear() + #settings.clear() error_message_box = QMessageBox() error_message_box.setWindowTitle('Error') error_message_box.setIcon(QMessageBox.Icon.Critical) diff --git a/taggui/utils/image.py b/taggui/utils/image.py index d2f251ed..43cfb8ef 100644 --- a/taggui/utils/image.py +++ b/taggui/utils/image.py @@ -12,7 +12,7 @@ class Image: target_dimension: tuple[int, int] | None = None # (x, y, width, height) crop: tuple[int, int, int, int] | None = None - hints: list[tuple[int, int, int, int]] = field(default_factory=list) - includes: list[tuple[int, int, int, int]] = field(default_factory=list) - excludes: list[tuple[int, int, int, int]] = field(default_factory=list) + hints: dict[str, tuple[int, int, int, int]] = field(default_factory=dict) + includes: dict[str, tuple[int, int, int, int]] = field(default_factory=dict) + excludes: dict[str, tuple[int, int, int, int]] = field(default_factory=dict) thumbnail: QIcon | None = None diff --git a/taggui/widgets/image_viewer.py b/taggui/widgets/image_viewer.py index 92dbae89..a9a9116a 100644 --- a/taggui/widgets/image_viewer.py +++ b/taggui/widgets/image_viewer.py @@ -294,7 +294,7 @@ def add_line_limit_lr(self, y: int, td: int): def add_hyperbola_limit(self, pos: QPoint, lr: int, td: int): target_area = settings.value('export_resolution', type=int)**2 - res_size = settings.value('export_bucket_res_size', type=int) + res_size = max(settings.value('export_bucket_res_size', type=int), 1) dx = res_size self.path.moveTo(pos.x() + lr * dx, pos.y() + td * target_area / dx) while dx * res_size <= target_area: @@ -368,13 +368,16 @@ def load_image(self, proxy_image_index: QModelIndex): self.scene.addItem(self.hud_item) if image.crop: - self.add_rectangle(QRect(*image.crop), ImageMarking.CROP, QSize(*image.target_dimension)) - for rect in image.hints: - self.add_rectangle(QRect(*rect), ImageMarking.HINT) - for rect in image.includes: - self.add_rectangle(QRect(*rect), ImageMarking.INCLUDE) - for rect in image.excludes: - self.add_rectangle(QRect(*rect), ImageMarking.EXCLUDE) + if not image.target_dimension: + image.target_dimension = target_dimension.get(image.crop[2:]) + self.add_rectangle(QRect(*image.crop), ImageMarking.CROP, + size=QSize(*image.target_dimension)) + for name, rect in image.hints.items(): + self.add_rectangle(QRect(*rect), ImageMarking.HINT, name=name) + for name, rect in image.includes.items(): + self.add_rectangle(QRect(*rect), ImageMarking.INCLUDE, name=name) + for name, rect in image.excludes.items(): + self.add_rectangle(QRect(*rect), ImageMarking.EXCLUDE, name=name) @Slot() def setting_change(self, key, value): @@ -387,13 +390,21 @@ def setting_change(self, key, value): def marking_change(self, rect: QGraphicsRectItem): assert self.proxy_image_index != None assert self.proxy_image_index.isValid() + image: Image = self.proxy_image_index.data(Qt.ItemDataRole.UserRole) + if rect.rect_type == ImageMarking.CROP: - image: Image = self.proxy_image_index.data(Qt.ItemDataRole.UserRole) image.thumbnail = None - image.crop = rect.rect().getRect() + image.crop = rect.rect().toRect().getRect() # ensure int! image.target_dimension = rect.target_size.toTuple() - self.proxy_image_list_model.dataChanged.emit(self.proxy_image_index, - self.proxy_image_index) + elif rect.rect_type == ImageMarking.HINT: + image.hints[rect.data(0)] = rect.rect().toRect().getRect() + elif rect.rect_type == ImageMarking.INCLUDE: + image.includes[rect.data(0)] = rect.rect().toRect().getRect() + elif rect.rect_type == ImageMarking.EXCLUDE: + image.excludes[rect.data(0)] = rect.rect().toRect().getRect() + + self.proxy_image_list_model.sourceModel().dataChanged.emit( + self.proxy_image_index, self.proxy_image_index) @Slot() def zoom_in(self, center_pos: QPoint = None): @@ -448,8 +459,10 @@ def wheelEvent(self, event): delta = new_pos - old_pos self.view.translate(delta.x(), delta.y()) - def add_rectangle(self, rect: QRect, rect_type: ImageMarking, size: QSize = None): + def add_rectangle(self, rect: QRect, rect_type: ImageMarking, + size: QSize = None, name: str = ''): rect_item = CustomRectItem(rect, rect_type, size) + rect_item.setData(0, name) if rect_type == ImageMarking.CROP: rect_item.signal.move.connect(self.hud_item.setValues) rect_item.signal.change.connect(self.marking_change) From 97fcaed4c45ddfc0f6793e52940cdf1271867724 Mon Sep 17 00:00:00 2001 From: StableLlama Date: Sat, 1 Mar 2025 22:35:50 +0100 Subject: [PATCH 26/82] Add toolbar entries to add markings and make the editable. --- taggui/models/image_list_model.py | 13 +- taggui/utils/target_dimension.py | 102 +++++---- taggui/widgets/image_tags_editor.py | 3 +- taggui/widgets/image_viewer.py | 344 ++++++++++++++++++++++++---- taggui/widgets/main_window.py | 90 +++++++- 5 files changed, 455 insertions(+), 97 deletions(-) diff --git a/taggui/models/image_list_model.py b/taggui/models/image_list_model.py index 621cea2f..13da55e8 100644 --- a/taggui/models/image_list_model.py +++ b/taggui/models/image_list_model.py @@ -9,8 +9,8 @@ import exifread import imagesize -from PySide6.QtCore import (QAbstractListModel, QModelIndex, QRect, QSize, Qt, - Signal, Slot) +from PySide6.QtCore import (QAbstractListModel, QModelIndex, QPoint, QRect, + QSize, Qt, Signal, Slot) from PySide6.QtGui import QIcon, QImageReader, QPixmap from PySide6.QtWidgets import QMessageBox @@ -89,7 +89,14 @@ def data(self, index, role=None) -> Image | str | QIcon | QSize: # Rotate the image based on the orientation tag. image_reader.setAutoTransform(True) if image.crop: - image_reader.setClipRect(QRect(*image.crop)) + crop = QRect(*image.crop) + else: + crop = QRect(QPoint(0, 0), image_reader.size()) + if crop.height() > crop.width()*3: + # keep it sane, higher than 3x the width doesn't make sense + crop.setTop((crop.height() - crop.width()*3)/2) # center crop + crop.setHeight(crop.width()*3) + image_reader.setClipRect(crop) pixmap = QPixmap.fromImageReader(image_reader).scaledToWidth( self.image_list_image_width, Qt.TransformationMode.SmoothTransformation) diff --git a/taggui/utils/target_dimension.py b/taggui/utils/target_dimension.py index f253ae7c..20e995dc 100644 --- a/taggui/utils/target_dimension.py +++ b/taggui/utils/target_dimension.py @@ -1,4 +1,4 @@ -from math import sqrt +from math import floor, sqrt import re from utils.settings import DEFAULT_SETTINGS, settings @@ -93,54 +93,32 @@ def get(dimensions: tuple[int, int]): preferred_sizes_bonus = 0.4 # reduce the loss by this factor max_pixels = resolution * resolution - opt_width = resolution * sqrt(width/height) - opt_height = resolution * sqrt(height/width) - if not upscaling: - opt_width = min(width, opt_width) - opt_height = min(height, opt_height) - - # test 1, guaranteed to find a solution: shrink and crop - # 1.1: exact width - candidate_width = max(opt_width // bucket_res, 1) * bucket_res - candidate_height = max((height * candidate_width / width) // bucket_res, 1) * bucket_res - loss = ((height * candidate_width / width) - candidate_height) * candidate_width - if (candidate_width, candidate_height) in _preferred_sizes: - loss *= preferred_sizes_bonus - # 1.2: exact height - test_height = max(opt_height // bucket_res, 1) * bucket_res - test_width = max((width * test_height / height) // bucket_res, 1) * bucket_res - test_loss = ((width * test_height / height) - test_width) * test_height - if (test_height, test_width) in _preferred_sizes: - test_loss *= preferred_sizes_bonus - if test_loss < loss: - candidate_width = test_width - candidate_height = test_height - loss = test_loss - - # test 2, going bigger might still fit in the size budget due to cropping - # 2.1: exact width - for delta in range(1, 10): - test_width = max(opt_width // bucket_res + delta, 1) * bucket_res + opt_width = floor(resolution * sqrt(width/height)) + opt_height = floor(resolution * sqrt(height/width)) + + loss = 1e10 + for dx, dy in [(0,0), (0,1), (1,0), (1,1)]: + opt_width += dx + opt_height += dy + + if not upscaling: + opt_width = min(width, opt_width) + opt_height = min(height, opt_height) + + # test 1, guaranteed to find a solution: shrink and crop + # 1.1: exact width + test_width = max(opt_width // bucket_res, 1) * bucket_res test_height = max((height * test_width / width) // bucket_res, 1) * bucket_res - if test_width * test_height > max_pixels: - break - if (test_width > width or test_height > height) and not upscaling: - break test_loss = ((height * test_width / width) - test_height) * test_width - if (test_height, test_width) in _preferred_sizes: + if (test_width, test_height) in _preferred_sizes: test_loss *= preferred_sizes_bonus - if test_loss < loss: - candidate_width = test_width - candidate_height = test_height - loss = test_loss - # 2.2: exact height - for delta in range(1, 10): - test_height = max(opt_height // bucket_res + delta, 1) * bucket_res + if test_loss < loss: + candidate_width = test_width + candidate_height = test_height + loss = test_loss + # 1.2: exact height + test_height = max(opt_height // bucket_res, 1) * bucket_res test_width = max((width * test_height / height) // bucket_res, 1) * bucket_res - if test_width * test_height > max_pixels: - break - if (test_width > width or test_height > height) and not upscaling: - break test_loss = ((width * test_height / height) - test_width) * test_height if (test_height, test_width) in _preferred_sizes: test_loss *= preferred_sizes_bonus @@ -149,4 +127,36 @@ def get(dimensions: tuple[int, int]): candidate_height = test_height loss = test_loss - return int(candidate_width), int(candidate_height) + # test 2, going bigger might still fit in the size budget due to cropping + # 2.1: exact width + for delta in range(1, 10): + test_width = max(opt_width // bucket_res + delta, 1) * bucket_res + test_height = max((height * test_width / width) // bucket_res, 1) * bucket_res + if test_width * test_height > max_pixels: + break + if (test_width > width or test_height > height) and not upscaling: + break + test_loss = ((height * test_width / width) - test_height) * test_width + if (test_height, test_width) in _preferred_sizes: + test_loss *= preferred_sizes_bonus + if test_loss < loss: + candidate_width = test_width + candidate_height = test_height + loss = test_loss + # 2.2: exact height + for delta in range(1, 10): + test_height = max(opt_height // bucket_res + delta, 1) * bucket_res + test_width = max((width * test_height / height) // bucket_res, 1) * bucket_res + if test_width * test_height > max_pixels: + break + if (test_width > width or test_height > height) and not upscaling: + break + test_loss = ((width * test_height / height) - test_width) * test_height + if (test_height, test_width) in _preferred_sizes: + test_loss *= preferred_sizes_bonus + if test_loss < loss: + candidate_width = test_width + candidate_height = test_height + loss = test_loss + + return int(candidate_width), int(candidate_height) diff --git a/taggui/widgets/image_tags_editor.py b/taggui/widgets/image_tags_editor.py index d7b05427..de517778 100644 --- a/taggui/widgets/image_tags_editor.py +++ b/taggui/widgets/image_tags_editor.py @@ -226,7 +226,8 @@ def reload_image_tags_if_changed(self, first_changed_index: QModelIndex, Reload the tags for the current image if its index is in the range of changed indices. """ - if (first_changed_index.row() <= self.image_index.row() + if (self.image_index and + first_changed_index.row() <= self.image_index.row() <= last_changed_index.row()): proxy_image_index = self.proxy_image_list_model.mapFromSource( self.image_index) diff --git a/taggui/widgets/image_viewer.py b/taggui/widgets/image_viewer.py index a9a9116a..0016b25e 100644 --- a/taggui/widgets/image_viewer.py +++ b/taggui/widgets/image_viewer.py @@ -6,7 +6,7 @@ Signal, Slot) from PySide6.QtGui import (QCursor, QColor, QPainter, QPainterPath, QPen, QPixmap, QTransform) -from PySide6.QtWidgets import (QGraphicsItem, QGraphicsPixmapItem, +from PySide6.QtWidgets import (QGraphicsItem, QGraphicsLineItem, QGraphicsPixmapItem, QGraphicsRectItem, QGraphicsScene, QGraphicsView, QVBoxLayout, QWidget) from utils.settings import settings @@ -15,11 +15,18 @@ import utils.target_dimension as target_dimension from dialogs.export_dialog import BucketStrategy +# Alignment base for a grid +base_point: QPoint = QPoint(0, 0) + +# stepsize of the grid +grid: int = 8 + class ImageMarking(str, Enum): CROP = 'crop' HINT = 'hint' INCLUDE = 'include in mask' EXCLUDE = 'exclude from mask' + NONE = 'no marking' class RectPosition(str, Enum): TL = 'top left' @@ -56,6 +63,63 @@ def flip_rect_position(pos: RectPosition, h_flip: bool, v_flip: bool) -> RectPos 20: RectPosition.BL, 21: RectPosition.BOTTOM, 22: RectPosition.BR, }[h+10*v] +def change_rect(rect: QRect, rect_pos: RectPosition, pos: QPoint) -> QRect: + if rect_pos == RectPosition.TL: + rect.setTopLeft(pos) + elif rect_pos == RectPosition.TOP: + rect.setTop(pos.y()) + elif rect_pos == RectPosition.TR: + rect.setTopRight(pos) + elif rect_pos == RectPosition.RIGHT: + rect.setRight(pos.x()) + elif rect_pos == RectPosition.BR: + rect.setBottomRight(pos) + elif rect_pos == RectPosition.BOTTOM: + rect.setBottom(pos.y()) + elif rect_pos == RectPosition.BL: + rect.setBottomLeft(pos) + elif rect_pos == RectPosition.LEFT: + rect.setLeft(pos.x()) + return rect + +def change_rect_to_match_size(rect: QRect, rect_pos: RectPosition, size: QSize) -> QRect: + """Change the `rect` at place `rect_pos` so that the size matches `size`. + + Moving a side will ignore the value in the size for the perpendicular side. + """ + rect_new = QRect(rect) + if rect_pos == RectPosition.TL: + rect_new.setSize(size) + rect_new.moveBottomRight(rect.bottomRight()) + elif rect_pos == RectPosition.TOP: + rect_new.setHeight(size.height()) + rect_new.moveBottom(rect.bottom()) + elif rect_pos == RectPosition.TR: + rect_new.setSize(size) + rect_new.moveBottomLeft(rect.bottomLeft()) + elif rect_pos == RectPosition.RIGHT: + rect_new.setWidth(size.width()) + rect_new.moveLeft(rect.left()) + elif rect_pos == RectPosition.BR: + rect_new.setSize(size) + rect_new.moveTopLeft(rect.topLeft()) + elif rect_pos == RectPosition.BOTTOM: + rect_new.setHeight(size.height()) + rect_new.moveTop(rect.top()) + elif rect_pos == RectPosition.BL: + rect_new.setSize(size) + rect_new.moveTopRight(rect.topRight()) + elif rect_pos == RectPosition.LEFT: + rect_new.setWidth(size.width()) + rect_new.moveRight(rect.right()) + return rect_new + +def map_to_grid(point: QPoint) -> QPoint: + """Align the point to the closest position on the grid aligned at + `base_point` and with a step width of `grid`. + """ + return (QPointF(point - base_point) / grid).toPoint() * grid + base_point + class RectItemSignal(QObject): change = Signal(QGraphicsRectItem, name='rectChanged') move = Signal(QRectF, RectPosition, name='rectIsMoving') @@ -68,7 +132,11 @@ class CustomRectItem(QGraphicsRectItem): handle_half_size = 5 zoom_factor = 1.0 # The size of the image this rect belongs to - image_size = QRectF(0, 0, 1, 1) + image_size = QRect(0, 0, 1, 1) + # the last (quantisized position of the mouse + #last_pos = None + # Static link to the single ImageGraphicsView in this application + image_view = None def __init__(self, rect: QRect, rect_type: ImageMarking, target_size: QSize | None = None, parent = None): @@ -84,7 +152,10 @@ def __init__(self, rect: QRect, rect_type: ImageMarking, ImageMarking.INCLUDE: Qt.green, ImageMarking.EXCLUDE: Qt.red, }[rect_type] - self.target_size = target_size + if rect_type == ImageMarking.CROP and target_size == None: + self.size_changed() # this method sets self.target_size + else: + self.target_size = target_size self.handle_selected = None self.mouse_press_pos = None self.mouse_press_rect = None @@ -93,7 +164,8 @@ def change_pen_half_width(self): self.prepareGeometryChange() def handleAt(self, point: QPointF) -> RectPosition: - handle_space = -min(self.pen_half_width - self.handle_half_size, 0)/self.zoom_factor + handle_space = -min(self.pen_half_width - self.handle_half_size, + 0)/self.zoom_factor left = point.x() < self.rect().left() + handle_space right = point.x() > self.rect().right() - handle_space top = point.y() < self.rect().top() + handle_space @@ -139,31 +211,69 @@ def mousePressEvent(self, event): self.mouse_press_scene_pos = event.scenePos() self.mouse_press_rect = self.rect() self.signal.move.emit(self.rect(), self.handle_selected) + if self.rect_type == ImageMarking.CROP: + global base_point + if (self.handle_selected == RectPosition.TL or + self.handle_selected == RectPosition.TOP): + base_point = self.rect().bottomRight().toPoint() + elif (self.handle_selected == RectPosition.TR or + self.handle_selected == RectPosition.RIGHT): + base_point = self.rect().bottomLeft().toPoint() + elif (self.handle_selected == RectPosition.BR or + self.handle_selected == RectPosition.BOTTOM): + base_point = self.rect().topLeft().toPoint() + elif (self.handle_selected == RectPosition.BL or + self.handle_selected == RectPosition.LEFT): + base_point = self.rect().topRight().toPoint() else: event.ignore() - super().mousePressEvent(event) def mouseMoveEvent(self, event): if self.handle_selected: - rect = self.rect() - pos_quantizised = event.scenePos().toPoint().toPointF() - if self.handle_selected == RectPosition.TL: - rect.setTopLeft(pos_quantizised) - elif self.handle_selected == RectPosition.TOP: - rect.setTop(pos_quantizised.y()) - elif self.handle_selected == RectPosition.TR: - rect.setTopRight(pos_quantizised) - elif self.handle_selected == RectPosition.RIGHT: - rect.setRight(pos_quantizised.x()) - elif self.handle_selected == RectPosition.BR: - rect.setBottomRight(pos_quantizised) - elif self.handle_selected == RectPosition.BOTTOM: - rect.setBottom(pos_quantizised.y()) - elif self.handle_selected == RectPosition.BL: - rect.setBottomLeft(pos_quantizised) - elif self.handle_selected == RectPosition.LEFT: - rect.setLeft(pos_quantizised.x()) - self.handle_selected = flip_rect_position(self.handle_selected, rect.width() < 0, rect.height() < 0) + if ((event.modifiers() & Qt.KeyboardModifier.ShiftModifier) == + Qt.KeyboardModifier.ShiftModifier): + if self.rect_type == ImageMarking.CROP: + pos_quantizised = event.pos().toPoint() + pos_quantizised_pre = pos_quantizised + rect_pre = change_rect(self.rect().toRect(), + self.handle_selected, + pos_quantizised) + target_width, target_height = target_dimension.get(rect_pre.size().toTuple()) + if rect_pre.height() * target_width / rect_pre.width() < target_height: # too wide + scale = rect_pre.height() / target_height + target_size0 = QSize(floor(target_width*scale), + rect_pre.height()) + target_size1 = QSize(floor(target_width*scale)+1, + rect_pre.height()) + else: # too high + scale = rect_pre.width() / target_width + target_size0 = QSize(rect_pre.width(), + floor(target_height*scale)) + target_size1 = QSize(rect_pre.width(), + floor(target_height*scale)+1) + tw0, th0 = target_dimension.get(target_size0.toTuple()) + tw1, th1 = target_dimension.get(target_size1.toTuple()) + if tw0 == target_width and th0 == target_height: + target_size = target_size0 + else: + target_size = target_size1 + rect = change_rect_to_match_size(self.rect().toRect(), + self.handle_selected, + target_size) + else: + pos_quantizised = map_to_grid(event.pos().toPoint()) + rect = change_rect(self.rect().toRect(), + self.handle_selected, + pos_quantizised) + else: + pos_quantizised = event.pos().toPoint() + rect = change_rect(self.rect().toRect(), + self.handle_selected, + pos_quantizised) + + self.handle_selected = flip_rect_position(self.handle_selected, + rect.width() < 0, + rect.height() < 0) if rect.width() == 0 or rect.height() == 0: self.setRect(rect) @@ -188,17 +298,27 @@ def paint(self, painter, option, widget=None): painter.setBrush(QColor(255, 0, 0, 127)) path = QPainterPath() path.addRect(self.rect()) - to_crop = self.rect().size() - self.target_size - path.addRect(QRectF(QPointF(self.rect().x()+floor(to_crop.width()/2), - self.rect().y()+floor(to_crop.height()/2)), self.target_size)) + if self.target_size: + to_crop = self.rect().size() - self.target_size + path.addRect(QRectF(QPointF(self.rect().x()+floor(to_crop.width()/2), + self.rect().y()+floor(to_crop.height()/2)), + self.target_size)) painter.drawPath(path) pen_half_width = self.pen_half_width / self.zoom_factor - pen = QPen(self.color, 2*pen_half_width, Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin) + pen = QPen(self.color, 2*pen_half_width, Qt.SolidLine, Qt.RoundCap, + Qt.RoundJoin) painter.setPen(pen) painter.setBrush(Qt.NoBrush) painter.drawRect(self.rect().adjusted(-pen_half_width, -pen_half_width, pen_half_width, pen_half_width)) + if self.isSelected(): + s_rect = self.rect().adjusted(-2*pen_half_width, -2*pen_half_width, + 2*pen_half_width, 2*pen_half_width) + painter.setPen(QPen(Qt.white, 1.5 / self.zoom_factor, Qt.SolidLine)) + painter.drawRect(s_rect) + painter.setPen(QPen(Qt.black, 1.5 / self.zoom_factor, Qt.DotLine)) + painter.drawRect(s_rect) def shape(self): path = super().shape() @@ -215,7 +335,7 @@ def boundingRect(self): def size_changed(self): if self.rect_type == ImageMarking.CROP: bucket_strategy = settings.value('export_bucket_strategy', type=str) - current = self.rect().size() + current = self.rect().size().expandedTo(QSize(1, 1)) if bucket_strategy == BucketStrategy.SCALE: self.target_size = current else: # CROP or CROP_SCALE @@ -224,14 +344,14 @@ def size_changed(self): scale = current.height() / target_height else: # too high scale = current.width() / target_width - self.target_size = QSize(target_width*scale, target_height*scale) + self.target_size = QSize(round(target_width*scale), round(target_height*scale)) if bucket_strategy == BucketStrategy.CROP_SCALE: self.target_size = (self.target_size + current.toSize())/2 class ResizeHintHUD(QGraphicsItem): zoom_factor = 1.0 - def __init__(self, boundingRect: QRectF, parent=None): + def __init__(self, boundingRect: QRect, parent=None): super().__init__(parent) self._boundingRect = boundingRect self.rect = QRectF(0, 0, 1, 1) @@ -321,8 +441,103 @@ def paint(self, painter, option, widget=None): painter.setPen(pen) painter.drawPath(self.path) +class ImageGraphicsView(QGraphicsView): + def __init__(self, scene, image_viewer): + super().__init__(scene) + self.setRenderHint(QPainter.Antialiasing) + self.setDragMode(QGraphicsView.DragMode.ScrollHandDrag) + self.setTransformationAnchor(QGraphicsView.ViewportAnchor.AnchorUnderMouse) + self.setResizeAnchor(QGraphicsView.ViewportAnchor.AnchorUnderMouse) + self.image_viewer = image_viewer + CustomRectItem.image_view = self + self.last_pos = None + self.clear_scene() + + def clear_scene(self): + """Use this and not scene.clear() due to resource management.""" + global grid, base_point + self.insertion_mode = False + self.horizontal_line = None + self.vertical_line = None + grid = 8 + base_point = QPoint(0, 0) + self.scene().clear() + + def set_insertion_mode(self, mode): + self.insertion_mode = mode + if mode: + self.setDragMode(QGraphicsView.DragMode.NoDrag) + self.setCursor(QCursor(Qt.CursorShape.CrossCursor)) + self.horizontal_line = QGraphicsLineItem() + self.vertical_line = QGraphicsLineItem() + self.scene().addItem(self.horizontal_line) + self.scene().addItem(self.vertical_line) + self.update_lines_pos() + else: + self.setDragMode(QGraphicsView.DragMode.ScrollHandDrag) + self.unsetCursor() + self.scene().removeItem(self.horizontal_line) + self.scene().removeItem(self.vertical_line) + + def update_lines_pos(self): + """Show the hint lines at the position self.last_pos. + + Note: do not use a position parameter as then the key event couldn't + immediately show them as the mouse position would be missing then. + """ + if self.last_pos: + view_rect = self.mapToScene(self.viewport().rect()).boundingRect() + self.horizontal_line.setLine(view_rect.left(), self.last_pos.y(), + view_rect.right(), self.last_pos.y()) + self.vertical_line.setLine(self.last_pos.x(), view_rect.top(), + self.last_pos.x(), view_rect.bottom()) + + + def mousePressEvent(self, event): + if self.insertion_mode and event.button() == Qt.MouseButton.LeftButton: + rect_type = self.image_viewer.marking_to_add + if rect_type == ImageMarking.NONE: + if ((event.modifiers() & Qt.KeyboardModifier.AltModifier) == + Qt.KeyboardModifier.AltModifier): + rect_type = ImageMarking.EXCLUDE + else: + rect_type = ImageMarking.HINT + + self.image_viewer.add_rectangle(QRect(self.last_pos, QSize(0, 0)), + rect_type) + self.set_insertion_mode(False) + super().mousePressEvent(event) + else: + super().mousePressEvent(event) + + def mouseMoveEvent(self, event): + last_pos_raw = self.mapToScene(event.position().toPoint()) + if ((event.modifiers() & Qt.KeyboardModifier.ShiftModifier) == + Qt.KeyboardModifier.ShiftModifier): + self.last_pos = map_to_grid(last_pos_raw.toPoint()) + else: + self.last_pos = last_pos_raw.toPoint() + pos = last_pos_raw + + if self.insertion_mode: + self.update_lines_pos() + else: + super().mouseMoveEvent(event) + + def keyPressEvent(self, event): + if event.key() == Qt.Key.Key_Control: + self.set_insertion_mode(True) + elif event.key() == Qt.Key.Key_Delete: + self.image_viewer.delete_selected() + + def keyReleaseEvent(self, event): + if event.key() == Qt.Key.Key_Control: + self.set_insertion_mode(False) + class ImageViewer(QWidget): zoom = Signal(float, name='zoomChanged') + marking = Signal(ImageMarking, name='markingToAdd') + accept_crop_addition = Signal(bool, name='allowAdditionOfCrop') def __init__(self, proxy_image_list_model: ProxyImageListModel): super().__init__() @@ -330,12 +545,9 @@ def __init__(self, proxy_image_list_model: ProxyImageListModel): CustomRectItem.pen_half_width = round(self.devicePixelRatio()) CustomRectItem.zoom_factor = 1.0 self.is_zoom_to_fit = True + self.marking_to_add = ImageMarking.NONE self.scene = QGraphicsScene() - self.view = QGraphicsView(self.scene) - self.view.setRenderHint(QPainter.Antialiasing) - self.view.setDragMode(QGraphicsView.ScrollHandDrag) - self.view.setTransformationAnchor(QGraphicsView.AnchorUnderMouse) - self.view.setResizeAnchor(QGraphicsView.AnchorUnderMouse) + self.view = ImageGraphicsView(self.scene, self) settings.change.connect(self.setting_change) layout = QVBoxLayout() @@ -351,7 +563,7 @@ def __init__(self, proxy_image_list_model: ProxyImageListModel): def load_image(self, proxy_image_index: QModelIndex): self.proxy_image_index = QPersistentModelIndex(proxy_image_index) - self.scene.clear() + self.view.clear_scene() if not self.proxy_image_index.isValid(): return @@ -361,12 +573,15 @@ def load_image(self, proxy_image_index: QModelIndex): self.scene.addItem(image_item) self.scene.setSceneRect(image_item.boundingRect() .adjusted(-1, -1, 1, 1)) # space for rect border - CustomRectItem.image_size = image_item.boundingRect() + CustomRectItem.image_size = image_item.boundingRect().toRect() self.zoom_fit() self.hud_item = ResizeHintHUD(CustomRectItem.image_size) self.scene.addItem(self.hud_item) + self.marking_to_add = ImageMarking.NONE + self.marking.emit(ImageMarking.NONE) + self.accept_crop_addition.emit(not image.crop) if image.crop: if not image.target_dimension: image.target_dimension = target_dimension.get(image.crop[2:]) @@ -388,6 +603,7 @@ def setting_change(self, key, value): @Slot(QGraphicsRectItem) def marking_change(self, rect: QGraphicsRectItem): + global grid, base_point assert self.proxy_image_index != None assert self.proxy_image_index.isValid() image: Image = self.proxy_image_index.data(Qt.ItemDataRole.UserRole) @@ -396,6 +612,10 @@ def marking_change(self, rect: QGraphicsRectItem): image.thumbnail = None image.crop = rect.rect().toRect().getRect() # ensure int! image.target_dimension = rect.target_size.toTuple() + scale = min(image.crop[2]/image.target_dimension[0], + image.crop[3]/image.target_dimension[1]) + base_point = QPoint(image.crop[0]+floor((image.crop[2]-scale*image.target_dimension[0])/2), + image.crop[1]+floor((image.crop[3]-scale*image.target_dimension[1])/2)) elif rect.rect_type == ImageMarking.HINT: image.hints[rect.data(0)] = rect.rect().toRect().getRect() elif rect.rect_type == ImageMarking.INCLUDE: @@ -447,6 +667,12 @@ def zoom_emit(self): else: self.zoom.emit(CustomRectItem.zoom_factor) + @Slot(ImageMarking) + def add_marking(self, marking: ImageMarking): + self.marking_to_add = marking + self.view.set_insertion_mode(marking != ImageMarking.NONE) + grid = 1 if marking == ImageMarking.CROP else 8 + def wheelEvent(self, event): old_pos = self.view.mapToScene(event.position().toPoint()) @@ -462,12 +688,52 @@ def wheelEvent(self, event): def add_rectangle(self, rect: QRect, rect_type: ImageMarking, size: QSize = None, name: str = ''): rect_item = CustomRectItem(rect, rect_type, size) - rect_item.setData(0, name) if rect_type == ImageMarking.CROP: rect_item.signal.move.connect(self.hud_item.setValues) + elif name == '' and rect_type != ImageMarking.NONE: + image: Image = self.proxy_image_index.data(Qt.ItemDataRole.UserRole) + if rect_type == ImageMarking.HINT: + keys = image.hints.keys() + pre = 'hint' + elif rect_type == ImageMarking.INCLUDE: + keys = image.includes.keys() + pre = 'include' + elif rect_type == ImageMarking.EXCLUDE: + keys = image.excludes.keys() + pre = 'exclude' + count = 1 + while f'{pre}{count}' in keys: + count += 1 + name = f'{pre}{count}' + rect_item.setData(0, name) rect_item.signal.change.connect(self.marking_change) self.scene.addItem(rect_item) self.rect_items.append(rect_item) + self.marking.emit(ImageMarking.NONE) + if rect_type == ImageMarking.CROP: + self.accept_crop_addition.emit(False) + self.marking_change(rect_item) + + @Slot() + def delete_selected(self): + image: Image = self.proxy_image_index.data(Qt.ItemDataRole.UserRole) + for item in self.scene.selectedItems(): + if item.rect_type == ImageMarking.CROP: + global base_point + image.thumbnail = None + image.crop = None + image.target_dimension = None + base_point = QPoint(0, 0) + self.accept_crop_addition.emit(True) + elif item.rect_type == ImageMarking.HINT: + del image.hints[item.data(0)] + elif item.rect_type == ImageMarking.INCLUDE: + del image.includes[item.data(0)] + elif item.rect_type == ImageMarking.EXCLUDE: + del image.excludes[item.data(0)] + self.scene.removeItem(item) + self.proxy_image_list_model.sourceModel().dataChanged.emit( + self.proxy_image_index, self.proxy_image_index) def resizeEvent(self, event): super().resizeEvent(event) diff --git a/taggui/widgets/main_window.py b/taggui/widgets/main_window.py index 167d55c5..1129b1a9 100644 --- a/taggui/widgets/main_window.py +++ b/taggui/widgets/main_window.py @@ -1,11 +1,14 @@ from pathlib import Path -from PySide6.QtCore import QKeyCombination, QModelIndex, QUrl, Qt, Slot -from PySide6.QtGui import (QAction, QCloseEvent, QDesktopServices, QIcon, - QKeySequence, QPixmap, QShortcut) +from PySide6.QtCore import (QKeyCombination, QModelIndex, QPoint, QRect, QUrl, + Qt, Slot) +from PySide6.QtGui import (QAction, QActionGroup, QColor, QCloseEvent, + QDesktopServices, QIcon, QKeySequence, QPainter, + QPainterPath, QPen, QPixmap, QShortcut) from PySide6.QtWidgets import (QApplication, QFileDialog, QMainWindow, QMessageBox, QStackedWidget, QToolBar, QVBoxLayout, QWidget) + from transformers import AutoTokenizer from dialogs.batch_reorder_tags_dialog import BatchReorderTagsDialog @@ -26,7 +29,7 @@ from widgets.auto_captioner import AutoCaptioner from widgets.image_list import ImageList from widgets.image_tags_editor import ImageTagsEditor -from widgets.image_viewer import ImageViewer +from widgets.image_viewer import ImageViewer, ImageMarking ICON_PATH = Path('images/icon.ico') GITHUB_REPOSITORY_URL = 'https://github.com/jhc13/taggui' @@ -69,17 +72,55 @@ def __init__(self, app: QApplication): self.toolbar.setObjectName('Main toolbar') self.toolbar.setFloatable(True) self.addToolBar(self.toolbar) - self.zoom_fit_best_action = QAction(QIcon.fromTheme('zoom-fit-best'), 'Zoom to fit', self) + self.zoom_fit_best_action = QAction(QIcon.fromTheme('zoom-fit-best'), + 'Zoom to fit', self) self.zoom_fit_best_action.setCheckable(True) self.toolbar.addAction(self.zoom_fit_best_action) - self.zoom_in_action = QAction(QIcon.fromTheme('zoom-in'), 'Zoom in', self) + self.zoom_in_action = QAction(QIcon.fromTheme('zoom-in'), + 'Zoom in', self) self.toolbar.addAction(self.zoom_in_action) - self.zoom_original_action = QAction(QIcon.fromTheme('zoom-original'), 'Original size', self) + self.zoom_original_action = QAction(QIcon.fromTheme('zoom-original'), + 'Original size', self) self.zoom_original_action.setCheckable(True) self.toolbar.addAction(self.zoom_original_action) - self.zoom_out_action = QAction(QIcon.fromTheme('zoom-out'), 'Zoom out', self) + self.zoom_out_action = QAction(QIcon.fromTheme('zoom-out'), + 'Zoom out', self) self.toolbar.addAction(self.zoom_out_action) self.toolbar.addSeparator() + self.add_action_group = QActionGroup(self) + self.add_action_group.setExclusionPolicy(QActionGroup.ExclusiveOptional) + self.add_crop_action = QAction(create_add_box_icon(Qt.blue), + 'Add crop', self.add_action_group) + self.add_crop_action.setCheckable(True) + self.toolbar.addAction(self.add_crop_action) + self.add_hint_action = QAction(create_add_box_icon(Qt.gray), + 'Add hint', self.add_action_group) + self.add_hint_action.setCheckable(True) + self.toolbar.addAction(self.add_hint_action) + self.add_include_action = QAction(create_add_box_icon(Qt.green), + 'Add include mask', self.add_action_group) + self.add_include_action.setCheckable(True) + self.toolbar.addAction(self.add_include_action) + self.add_exclude_action = QAction(create_add_box_icon(Qt.red), + 'Add exclude mask', self.add_action_group) + self.add_exclude_action.setCheckable(True) + self.toolbar.addAction(self.add_exclude_action) + self.image_viewer.marking.connect(lambda marking: + self.add_crop_action.setChecked(True) if marking == ImageMarking.CROP else + self.add_hint_action.setChecked(True) if marking == ImageMarking.HINT else + self.add_include_action.setChecked(True) if marking == ImageMarking.INCLUDE else + self.add_exclude_action.setChecked(True) if marking == ImageMarking.EXCLUDE else + self.add_action_group.checkedAction() and + self.add_action_group.checkedAction().setChecked(False)) + self.image_viewer.accept_crop_addition.connect(self.add_crop_action.setEnabled) + self.delete_marking_action = QAction(QIcon.fromTheme('edit-delete'), + 'Delete marking', self) + self.delete_marking_action.setEnabled(False) + self.toolbar.addAction(self.delete_marking_action) + self.image_viewer.scene.selectionChanged.connect(lambda: + self.delete_marking_action.setEnabled( + len(self.image_viewer.scene.selectedItems())>0)) + self.image_list = ImageList(self.proxy_image_list_model, tag_separator, image_list_image_width) @@ -482,6 +523,14 @@ def connect_toolbar_signals(self): self.toolbar.visibilityChanged.connect( lambda: self.toggle_toolbar_action.setChecked( self.toolbar.isVisible())) + self.add_action_group.triggered.connect( + lambda action: self.image_viewer.add_marking( + ImageMarking.NONE if not action.isChecked() else + ImageMarking.CROP if action == self.add_crop_action else + ImageMarking.HINT if action == self.add_hint_action else + ImageMarking.INCLUDE if action == self.add_include_action else + ImageMarking.EXCLUDE)) + self.delete_marking_action.triggered.connect(self.image_viewer.delete_selected) def connect_image_list_signals(self): self.image_list.filter_line_edit.textChanged.connect( @@ -631,3 +680,28 @@ def restore(self): type=str)) if directory_path.is_dir(): self.load_directory(directory_path, select_index=image_index) + +def create_add_box_icon(color: QColor) -> QPixmap: + """Create a QPixmap for an icon""" + pixmap = QPixmap(32, 32) + pixmap.fill(QColor('transparent')) + + # Create a painter to draw on the pixmap + painter = QPainter(pixmap) + + # Draw a bordered rectangle in the specified color + rect = QRect(2, 2, 28, 28) + painter.setPen(QPen(color, 2)) + painter.drawRect(rect) + + # Draw a plus sign in the middle + painter.setPen(QPen(Qt.black, 1)) + path = QPainterPath() + path.moveTo(16, 10) + path.lineTo(16, 22) + path.moveTo(10, 16) + path.lineTo(22, 16) + painter.drawPath(path) + painter.end() + + return pixmap From 8ba40c3c15c71d24074ec463ba2be6d40a25aee7 Mon Sep 17 00:00:00 2001 From: StableLlama Date: Sun, 2 Mar 2025 15:19:17 +0100 Subject: [PATCH 27/82] Make label display working as intended. Also rename CustomRectItem to MarkingItem --- taggui/widgets/image_viewer.py | 140 ++++++++++++++++++++++++--------- taggui/widgets/main_window.py | 2 +- 2 files changed, 102 insertions(+), 40 deletions(-) diff --git a/taggui/widgets/image_viewer.py b/taggui/widgets/image_viewer.py index 0016b25e..1539fb53 100644 --- a/taggui/widgets/image_viewer.py +++ b/taggui/widgets/image_viewer.py @@ -6,8 +6,9 @@ Signal, Slot) from PySide6.QtGui import (QCursor, QColor, QPainter, QPainterPath, QPen, QPixmap, QTransform) -from PySide6.QtWidgets import (QGraphicsItem, QGraphicsLineItem, QGraphicsPixmapItem, - QGraphicsRectItem, QGraphicsScene, QGraphicsView, +from PySide6.QtWidgets import (QGraphicsItem, QGraphicsLineItem, + QGraphicsPixmapItem, QGraphicsRectItem, + QGraphicsTextItem, QGraphicsScene, QGraphicsView, QVBoxLayout, QWidget) from utils.settings import settings from models.proxy_image_list_model import ProxyImageListModel @@ -124,7 +125,7 @@ class RectItemSignal(QObject): change = Signal(QGraphicsRectItem, name='rectChanged') move = Signal(QRectF, RectPosition, name='rectIsMoving') -class CustomRectItem(QGraphicsRectItem): +class MarkingItem(QGraphicsRectItem): # the halfed size of the pen in local coordinates to make sure it stays the # same during zooming pen_half_width = 1.0 @@ -146,6 +147,7 @@ def __init__(self, rect: QRect, rect_type: ImageMarking, self.setAcceptHoverEvents(True) self.signal = RectItemSignal() self.rect_type = rect_type + self.label: MarkingLabel | None = None self.color = { ImageMarking.CROP: Qt.blue, ImageMarking.HINT: Qt.gray, @@ -160,9 +162,6 @@ def __init__(self, rect: QRect, rect_type: ImageMarking, self.mouse_press_pos = None self.mouse_press_rect = None - def change_pen_half_width(self): - self.prepareGeometryChange() - def handleAt(self, point: QPointF) -> RectPosition: handle_space = -min(self.pen_half_width - self.handle_half_size, 0)/self.zoom_factor @@ -312,6 +311,7 @@ def paint(self, painter, option, widget=None): painter.setBrush(Qt.NoBrush) painter.drawRect(self.rect().adjusted(-pen_half_width, -pen_half_width, pen_half_width, pen_half_width)) + if self.isSelected(): s_rect = self.rect().adjusted(-2*pen_half_width, -2*pen_half_width, 2*pen_half_width, 2*pen_half_width) @@ -347,6 +347,58 @@ def size_changed(self): self.target_size = QSize(round(target_width*scale), round(target_height*scale)) if bucket_strategy == BucketStrategy.CROP_SCALE: self.target_size = (self.target_size + current.toSize())/2 + self.adjust_layout() + + def adjust_layout(self): + if self.label != None: + self.label.changeZoom(self.zoom_factor) + pen_half_width = self.pen_half_width / self.zoom_factor + if self.rect().y() > self.label.boundingRect().height(): + self.label.setPos(self.rect().adjusted( + -2 * pen_half_width, + -pen_half_width - self.label.boundingRect().height(), + 0, 0).topLeft()) + self.label.parentItem().setRect(self.label.sceneBoundingRect()) + else: + self.label.setPos(self.rect().adjusted( + -pen_half_width, -pen_half_width, 0, 0).topLeft()) + self.label.parentItem().setRect(self.label.sceneBoundingRect()) + + +class MarkingLabel(QGraphicsTextItem): + editingFinished = Signal(str) + + def __init__(self, text, parent): + super().__init__(text, parent) + self.setDefaultTextColor(Qt.black) + self.setTextInteractionFlags(Qt.TextEditorInteraction) + + def focusOutEvent(self, event): + super().focusOutEvent(event) + self.editingFinished.emit(self.toPlainText()) + + def keyPressEvent(self, event): + super().keyPressEvent(event) + self.parentItem().setRect(self.sceneBoundingRect()) + if event.key() in (Qt.Key_Enter, Qt.Key_Return): + self.clearFocus() + self.editingFinished.emit(self.toPlainText()) + + def insertFromMimeData(self, source): + if source.hasText(): + # Insert only the plain text + cursor = self.textCursor() + cursor.insertText(source.text()) + else: + super().insertFromMimeData(source) + self.parentItem().setRect(self.sceneBoundingRect()) + + def changeZoom(self, zoom_factor): + font = self.font() + font.setPointSizeF(10 / zoom_factor) + self.setFont(font) + self.parentItem().setRect(self.sceneBoundingRect()) + class ResizeHintHUD(QGraphicsItem): zoom_factor = 1.0 @@ -449,7 +501,7 @@ def __init__(self, scene, image_viewer): self.setTransformationAnchor(QGraphicsView.ViewportAnchor.AnchorUnderMouse) self.setResizeAnchor(QGraphicsView.ViewportAnchor.AnchorUnderMouse) self.image_viewer = image_viewer - CustomRectItem.image_view = self + MarkingItem.image_view = self self.last_pos = None self.clear_scene() @@ -529,10 +581,12 @@ def keyPressEvent(self, event): self.set_insertion_mode(True) elif event.key() == Qt.Key.Key_Delete: self.image_viewer.delete_selected() + super().keyPressEvent(event) def keyReleaseEvent(self, event): if event.key() == Qt.Key.Key_Control: self.set_insertion_mode(False) + super().keyReleaseEvent(event) class ImageViewer(QWidget): zoom = Signal(float, name='zoomChanged') @@ -542,8 +596,8 @@ class ImageViewer(QWidget): def __init__(self, proxy_image_list_model: ProxyImageListModel): super().__init__() self.proxy_image_list_model = proxy_image_list_model - CustomRectItem.pen_half_width = round(self.devicePixelRatio()) - CustomRectItem.zoom_factor = 1.0 + MarkingItem.pen_half_width = round(self.devicePixelRatio()) + MarkingItem.zoom_factor = 1.0 self.is_zoom_to_fit = True self.marking_to_add = ImageMarking.NONE self.scene = QGraphicsScene() @@ -555,7 +609,7 @@ def __init__(self, proxy_image_list_model: ProxyImageListModel): self.setLayout(layout) self.proxy_image_index: QPersistentModelIndex = None - self.rect_items: list[CustomRectItem] = [] + self.marking_items: list[MarkingItem] = [] self.view.wheelEvent = self.wheelEvent @@ -573,10 +627,10 @@ def load_image(self, proxy_image_index: QModelIndex): self.scene.addItem(image_item) self.scene.setSceneRect(image_item.boundingRect() .adjusted(-1, -1, 1, 1)) # space for rect border - CustomRectItem.image_size = image_item.boundingRect().toRect() + MarkingItem.image_size = image_item.boundingRect().toRect() self.zoom_fit() - self.hud_item = ResizeHintHUD(CustomRectItem.image_size) + self.hud_item = ResizeHintHUD(MarkingItem.image_size) self.scene.addItem(self.hud_item) self.marking_to_add = ImageMarking.NONE @@ -597,38 +651,38 @@ def load_image(self, proxy_image_index: QModelIndex): @Slot() def setting_change(self, key, value): if key == 'export_bucket_strategy': - for rect in self.rect_items: - rect.size_changed() + for marking in self.marking_items: + marking.size_changed() self.scene.invalidate() @Slot(QGraphicsRectItem) - def marking_change(self, rect: QGraphicsRectItem): + def marking_change(self, marking: QGraphicsRectItem): global grid, base_point assert self.proxy_image_index != None assert self.proxy_image_index.isValid() image: Image = self.proxy_image_index.data(Qt.ItemDataRole.UserRole) - if rect.rect_type == ImageMarking.CROP: + if marking.rect_type == ImageMarking.CROP: image.thumbnail = None - image.crop = rect.rect().toRect().getRect() # ensure int! - image.target_dimension = rect.target_size.toTuple() + image.crop = marking.rect().toRect().getRect() # ensure int! + image.target_dimension = marking.target_size.toTuple() scale = min(image.crop[2]/image.target_dimension[0], image.crop[3]/image.target_dimension[1]) base_point = QPoint(image.crop[0]+floor((image.crop[2]-scale*image.target_dimension[0])/2), image.crop[1]+floor((image.crop[3]-scale*image.target_dimension[1])/2)) - elif rect.rect_type == ImageMarking.HINT: - image.hints[rect.data(0)] = rect.rect().toRect().getRect() - elif rect.rect_type == ImageMarking.INCLUDE: - image.includes[rect.data(0)] = rect.rect().toRect().getRect() - elif rect.rect_type == ImageMarking.EXCLUDE: - image.excludes[rect.data(0)] = rect.rect().toRect().getRect() + elif marking.rect_type == ImageMarking.HINT: + image.hints[marking.data(0)] = marking.rect().toRect().getRect() + elif marking.rect_type == ImageMarking.INCLUDE: + image.includes[marking.data(0)] = marking.rect().toRect().getRect() + elif marking.rect_type == ImageMarking.EXCLUDE: + image.excludes[marking.data(0)] = marking.rect().toRect().getRect() self.proxy_image_list_model.sourceModel().dataChanged.emit( self.proxy_image_index, self.proxy_image_index) @Slot() def zoom_in(self, center_pos: QPoint = None): - CustomRectItem.zoom_factor = min(CustomRectItem.zoom_factor * 1.25, 16) + MarkingItem.zoom_factor = min(MarkingItem.zoom_factor * 1.25, 16) self.is_zoom_to_fit = False self.zoom_emit() @@ -636,7 +690,7 @@ def zoom_in(self, center_pos: QPoint = None): def zoom_out(self, center_pos: QPoint = None): view = self.view.viewport().size() scene = self.scene.sceneRect() - CustomRectItem.zoom_factor = max(CustomRectItem.zoom_factor / 1.25, + MarkingItem.zoom_factor = max(MarkingItem.zoom_factor / 1.25, min(view.width()/scene.width(), view.height()/scene.height())) self.is_zoom_to_fit = False @@ -644,28 +698,30 @@ def zoom_out(self, center_pos: QPoint = None): @Slot() def zoom_original(self): - CustomRectItem.zoom_factor = 1.0 + MarkingItem.zoom_factor = 1.0 self.is_zoom_to_fit = False self.zoom_emit() @Slot() def zoom_fit(self): self.view.fitInView(self.scene.sceneRect(), Qt.KeepAspectRatio) - CustomRectItem.zoom_factor = self.view.transform().m11() + MarkingItem.zoom_factor = self.view.transform().m11() self.is_zoom_to_fit = True self.zoom_emit() def zoom_emit(self): - ResizeHintHUD.zoom_factor = CustomRectItem.zoom_factor + ResizeHintHUD.zoom_factor = MarkingItem.zoom_factor transform = self.view.transform() self.view.setTransform(QTransform( - CustomRectItem.zoom_factor, transform.m12(), transform.m13(), - transform.m21(), CustomRectItem.zoom_factor, transform.m23(), + MarkingItem.zoom_factor, transform.m12(), transform.m13(), + transform.m21(), MarkingItem.zoom_factor, transform.m23(), transform.m31(), transform.m32(), transform.m33())) + for marking in self.marking_items: + marking.adjust_layout() if self.is_zoom_to_fit: self.zoom.emit(-1) else: - self.zoom.emit(CustomRectItem.zoom_factor) + self.zoom.emit(MarkingItem.zoom_factor) @Slot(ImageMarking) def add_marking(self, marking: ImageMarking): @@ -687,9 +743,9 @@ def wheelEvent(self, event): def add_rectangle(self, rect: QRect, rect_type: ImageMarking, size: QSize = None, name: str = ''): - rect_item = CustomRectItem(rect, rect_type, size) + marking_item = MarkingItem(rect, rect_type, size) if rect_type == ImageMarking.CROP: - rect_item.signal.move.connect(self.hud_item.setValues) + marking_item.signal.move.connect(self.hud_item.setValues) elif name == '' and rect_type != ImageMarking.NONE: image: Image = self.proxy_image_index.data(Qt.ItemDataRole.UserRole) if rect_type == ImageMarking.HINT: @@ -705,14 +761,20 @@ def add_rectangle(self, rect: QRect, rect_type: ImageMarking, while f'{pre}{count}' in keys: count += 1 name = f'{pre}{count}' - rect_item.setData(0, name) - rect_item.signal.change.connect(self.marking_change) - self.scene.addItem(rect_item) - self.rect_items.append(rect_item) + marking_item.setData(0, name) + if rect_type != ImageMarking.CROP and rect_type != ImageMarking.NONE: + label_background = QGraphicsRectItem(marking_item) + label_background.setBrush(marking_item.color) + label_background.setPen(Qt.NoPen) + marking_item.label = MarkingLabel(name, label_background) + marking_item.adjust_layout() + marking_item.signal.change.connect(self.marking_change) + self.scene.addItem(marking_item) + self.marking_items.append(marking_item) self.marking.emit(ImageMarking.NONE) if rect_type == ImageMarking.CROP: self.accept_crop_addition.emit(False) - self.marking_change(rect_item) + self.marking_change(marking_item) @Slot() def delete_selected(self): diff --git a/taggui/widgets/main_window.py b/taggui/widgets/main_window.py index 1129b1a9..7a82fc4c 100644 --- a/taggui/widgets/main_window.py +++ b/taggui/widgets/main_window.py @@ -118,7 +118,7 @@ def __init__(self, app: QApplication): self.delete_marking_action.setEnabled(False) self.toolbar.addAction(self.delete_marking_action) self.image_viewer.scene.selectionChanged.connect(lambda: - self.delete_marking_action.setEnabled( + self.image_viewer.scene and self.delete_marking_action.setEnabled( len(self.image_viewer.scene.selectedItems())>0)) From a2571b6971c6fd0e45ff928468c343d97cc7fcb1 Mon Sep 17 00:00:00 2001 From: StableLlama Date: Sun, 2 Mar 2025 19:34:55 +0100 Subject: [PATCH 28/82] Change JSON format to list of markings Also refactor a bit to use QSize instead of a simple tuple --- taggui/dialogs/export_dialog.py | 16 ++--- taggui/models/image_list_model.py | 50 +++++--------- taggui/utils/image.py | 26 ++++++-- taggui/utils/target_dimension.py | 16 +++-- taggui/widgets/image_viewer.py | 106 +++++++++++++----------------- 5 files changed, 100 insertions(+), 114 deletions(-) diff --git a/taggui/dialogs/export_dialog.py b/taggui/dialogs/export_dialog.py index c6a84839..9ffab2e4 100644 --- a/taggui/dialogs/export_dialog.py +++ b/taggui/dialogs/export_dialog.py @@ -6,7 +6,7 @@ from pathlib import Path import shutil -from PySide6.QtCore import Qt, Slot +from PySide6.QtCore import QSize, Qt, Slot from PySide6.QtGui import QColorSpace from PySide6.QtWidgets import (QWidget, QDialog, QFileDialog, QGridLayout, QLabel, QLineEdit, QPushButton, QTableWidget, @@ -368,8 +368,8 @@ def show_statistics(self): image_dimensions = defaultdict(int) for this_image in image_list: this_image.target_dimension = target_dimension.get( - this_image.dimensions) - image_dimensions[this_image.target_dimension] += 1 + QSize(*this_image.dimensions)) + image_dimensions[this_image.target_dimension.toTuple()] += 1 self.image_list.proxy_image_list_model.invalidate() sorted_dimensions = sorted( @@ -527,7 +527,7 @@ def do_export(self): else: image_file = image_file.convert("RGB") # Otherwise, convert to RGB - new_width, new_height = image_entry.target_dimension + new_width, new_height = image_entry.target_dimension.toTuple() current_width, current_height = image_file.size if bucket_strategy == BucketStrategy.CROP or bucket_strategy == BucketStrategy.CROP_SCALE: if current_height * new_width / current_width < new_height: # too wide @@ -535,8 +535,8 @@ def do_export(self): else: # too high new_height = floor(current_height * new_width / current_width) if bucket_strategy == BucketStrategy.CROP_SCALE: - new_width = floor((image_entry.target_dimension[0] + new_width)/2) - new_height = floor((image_entry.target_dimension[1] + new_height)/2) + new_width = floor((image_entry.target_dimension.width() + new_width)/2) + new_height = floor((image_entry.target_dimension.height() + new_height)/2) if image_file.size[0] != new_width or image_file.size[1] != new_height: # resize with the best method available resized_image = image_file.resize((new_width, new_height), Image.LANCZOS) @@ -547,8 +547,8 @@ def do_export(self): # crop to the desired size current_width, current_height = sharpend_image.size - crop_width = floor((current_width - image_entry.target_dimension[0]) / 2) - crop_height = floor((current_height - image_entry.target_dimension[1]) / 2) + crop_width = floor((current_width - image_entry.target_dimension.width()) / 2) + crop_height = floor((current_height - image_entry.target_dimension.height()) / 2) cropped_image = sharpend_image.crop((crop_width, crop_height, current_width - crop_width, current_height - crop_height)) lossless = quality > 99 diff --git a/taggui/models/image_list_model.py b/taggui/models/image_list_model.py index 13da55e8..ab777af8 100644 --- a/taggui/models/image_list_model.py +++ b/taggui/models/image_list_model.py @@ -14,7 +14,7 @@ from PySide6.QtGui import QIcon, QImageReader, QPixmap from PySide6.QtWidgets import QMessageBox -from utils.image import Image +from utils.image import Image, ImageMarking, Marking from utils.settings import DEFAULT_SETTINGS, settings from utils.utils import get_confirmation_dialog_reply, pluralize @@ -89,7 +89,7 @@ def data(self, index, role=None) -> Image | str | QIcon | QSize: # Rotate the image based on the orientation tag. image_reader.setAutoTransform(True) if image.crop: - crop = QRect(*image.crop) + crop = image.crop else: crop = QRect(QPoint(0, 0), image_reader.size()) if crop.height() > crop.width()*3: @@ -193,32 +193,16 @@ def load_directory(self, directory_path: Path): if meta.get('version') == 1: crop = meta.get('crop') if crop and type(crop) is list and len(crop) == 4: - image.crop = tuple(crop) + image.crop = QRect(*crop) target_dimension = meta.get('target_dimension') if (target_dimension and type(target_dimension) is list and len(target_dimension) == 2): - image.target_dimension = tuple(target_dimension) - hints = meta.get('hints') - if hints and type(hints) is dict: - image.hints = {} - for key, value in hints.items(): - if (value and type(value) is list and - len(value) == 4): - image.hints[key] = tuple(value) - includes = meta.get('includes') - if includes and type(includes) is dict: - image.includes = {} - for key, value in includes.items(): - if (value and type(value) is list and - len(value) == 4): - image.includes[key] = tuple(value) - excludes = meta.get('excludes') - if excludes and type(excludes) is dict: - image.excludes = {} - for key, value in excludes.items(): - if (value and type(value) is list and - len(value) == 4): - image.excludes[key] = tuple(value) + image.target_dimension = QSize(*target_dimension) + markings = meta.get('markings') + if markings and type(markings) is list: + for marking in markings: + marking = Marking(marking.get('label'), ImageMarking[marking.get('type')], QRect(*marking.get('rect'))) + image.markings.append(marking) else: error_messages.append(f'Invalid version ' f'"{meta.get('version')}" in ' @@ -259,15 +243,15 @@ def write_meta_to_disk(self, image: Image): does_exist = image.path.with_suffix('.json').exists() meta = {'version': 1} if image.crop: - meta['crop'] = image.crop + meta['crop'] = image.crop.getRect() if image.target_dimension: - meta['target_dimension'] = image.target_dimension - if image.hints: - meta['hints'] = image.hints - if image.includes: - meta['includes'] = image.includes - if image.excludes: - meta['excludes'] = image.excludes + meta['target_dimension'] = image.target_dimension.toTuple() + markings: list[dict[str, any]] = [] + for marking in image.markings: + markings.append({'label': marking.label, + 'type': marking.type.name, + 'rect': marking.rect.getRect()}) + meta['markings'] = markings if does_exist or len(meta.keys()) > 1: try: with image.path.with_suffix('.json').open('w', encoding='UTF-8') as meta_file: diff --git a/taggui/utils/image.py b/taggui/utils/image.py index 43cfb8ef..af5b3b0d 100644 --- a/taggui/utils/image.py +++ b/taggui/utils/image.py @@ -1,18 +1,32 @@ +from enum import Enum from dataclasses import dataclass, field from pathlib import Path +from PySide6.QtCore import QRect, QSize from PySide6.QtGui import QIcon +class ImageMarking(str, Enum): + CROP = 'crop' + HINT = 'hint' + INCLUDE = 'include in mask' + EXCLUDE = 'exclude from mask' + NONE = 'no marking' + + +@dataclass +class Marking: + label: str + type: ImageMarking + rect: QRect + + @dataclass class Image: path: Path dimensions: tuple[int, int] | None tags: list[str] = field(default_factory=list) - target_dimension: tuple[int, int] | None = None - # (x, y, width, height) - crop: tuple[int, int, int, int] | None = None - hints: dict[str, tuple[int, int, int, int]] = field(default_factory=dict) - includes: dict[str, tuple[int, int, int, int]] = field(default_factory=dict) - excludes: dict[str, tuple[int, int, int, int]] = field(default_factory=dict) + target_dimension: QSize | None = None + crop: QRect | None = None + markings: list[Marking] = field(default_factory=list) thumbnail: QIcon | None = None diff --git a/taggui/utils/target_dimension.py b/taggui/utils/target_dimension.py index 20e995dc..fe2487a1 100644 --- a/taggui/utils/target_dimension.py +++ b/taggui/utils/target_dimension.py @@ -1,10 +1,12 @@ from math import floor, sqrt import re +from PySide6.QtCore import QSize + from utils.settings import DEFAULT_SETTINGS, settings # singleton data store -_preferred_sizes : list[tuple[int, int]] = [] +_preferred_sizes : list[QSize] = [] settings.change.connect(lambda: _preferred_sizes.clear()) @@ -15,7 +17,7 @@ def get_preferred_sizes(): return _preferred_sizes -def prepare(aspect_ratios : list[tuple[int, int, int]] | None = None) -> list[tuple[int, int, int]] | None: +def prepare(aspect_ratios : list[tuple[int, int, float]] | None = None) -> list[tuple[int, int, float]] | None: """ Prepare by parsing the user supplied preferred sizes. @@ -56,7 +58,7 @@ def prepare(aspect_ratios : list[tuple[int, int, int]] | None = None) -> list[tu continue # Skip to the next resolution if there's an error return aspect_ratios -def get(dimensions: tuple[int, int]): +def get(dimensions: QSize) -> QSize: """ Determine the dimensions of an image it should have when it is exported. @@ -65,11 +67,11 @@ def get(dimensions: tuple[int, int]): Parameters ---------- - dimensions : tuple[int, int] + dimensions: QSize The width and height of the image """ global _preferred_sizes - width, height = dimensions + width, height = dimensions.toTuple() # The target resolution of the AI model. The target image pixels # will not exceed the square of this number resolution = settings.value('export_resolution', defaultValue=DEFAULT_SETTINGS['export_resolution'], type=int) @@ -83,7 +85,7 @@ def get(dimensions: tuple[int, int]): if resolution == 0: # no rescale in this case, only cropping - return ((width // bucket_res)*bucket_res, (height // bucket_res)*bucket_res) + return QSize((width // bucket_res)*bucket_res, (height // bucket_res)*bucket_res) if width < bucket_res or height < bucket_res: # it doesn't make sense to use such a small image. But we shouldn't @@ -159,4 +161,4 @@ def get(dimensions: tuple[int, int]): candidate_height = test_height loss = test_loss - return int(candidate_width), int(candidate_height) + return QSize(candidate_width, candidate_height) diff --git a/taggui/widgets/image_viewer.py b/taggui/widgets/image_viewer.py index 1539fb53..a07f24b1 100644 --- a/taggui/widgets/image_viewer.py +++ b/taggui/widgets/image_viewer.py @@ -12,7 +12,7 @@ QVBoxLayout, QWidget) from utils.settings import settings from models.proxy_image_list_model import ProxyImageListModel -from utils.image import Image +from utils.image import Image, ImageMarking, Marking import utils.target_dimension as target_dimension from dialogs.export_dialog import BucketStrategy @@ -22,13 +22,6 @@ # stepsize of the grid grid: int = 8 -class ImageMarking(str, Enum): - CROP = 'crop' - HINT = 'hint' - INCLUDE = 'include in mask' - EXCLUDE = 'exclude from mask' - NONE = 'no marking' - class RectPosition(str, Enum): TL = 'top left' TOP = 'top' @@ -237,22 +230,21 @@ def mouseMoveEvent(self, event): rect_pre = change_rect(self.rect().toRect(), self.handle_selected, pos_quantizised) - target_width, target_height = target_dimension.get(rect_pre.size().toTuple()) - if rect_pre.height() * target_width / rect_pre.width() < target_height: # too wide - scale = rect_pre.height() / target_height - target_size0 = QSize(floor(target_width*scale), + target = target_dimension.get(rect_pre.size()) + if rect_pre.height() * target.width() / rect_pre.width() < target.height(): # too wide + scale = rect_pre.height() / target.height() + target_size0 = QSize(floor(target.width()*scale), rect_pre.height()) - target_size1 = QSize(floor(target_width*scale)+1, + target_size1 = QSize(floor(target.width()*scale)+1, rect_pre.height()) else: # too high - scale = rect_pre.width() / target_width + scale = rect_pre.width() / target.width() target_size0 = QSize(rect_pre.width(), - floor(target_height*scale)) + floor(target.height()*scale)) target_size1 = QSize(rect_pre.width(), - floor(target_height*scale)+1) - tw0, th0 = target_dimension.get(target_size0.toTuple()) - tw1, th1 = target_dimension.get(target_size1.toTuple()) - if tw0 == target_width and th0 == target_height: + floor(target.height()*scale)+1) + t0 = target_dimension.get(target_size0) + if t0 == target: target_size = target_size0 else: target_size = target_size1 @@ -339,12 +331,12 @@ def size_changed(self): if bucket_strategy == BucketStrategy.SCALE: self.target_size = current else: # CROP or CROP_SCALE - target_width, target_height = target_dimension.get(current.toTuple()) - if current.height() * target_width / current.width() < target_height: # too wide - scale = current.height() / target_height + target = target_dimension.get(current) + if current.height() * target.width() / current.width() < target.height(): # too wide + scale = current.height() / target.height() else: # too high - scale = current.width() / target_width - self.target_size = QSize(round(target_width*scale), round(target_height*scale)) + scale = current.width() / target.width() + self.target_size = QSize(round(target.width()*scale), round(target.height()*scale)) if bucket_strategy == BucketStrategy.CROP_SCALE: self.target_size = (self.target_size + current.toSize())/2 self.adjust_layout() @@ -638,15 +630,11 @@ def load_image(self, proxy_image_index: QModelIndex): self.accept_crop_addition.emit(not image.crop) if image.crop: if not image.target_dimension: - image.target_dimension = target_dimension.get(image.crop[2:]) - self.add_rectangle(QRect(*image.crop), ImageMarking.CROP, - size=QSize(*image.target_dimension)) - for name, rect in image.hints.items(): - self.add_rectangle(QRect(*rect), ImageMarking.HINT, name=name) - for name, rect in image.includes.items(): - self.add_rectangle(QRect(*rect), ImageMarking.INCLUDE, name=name) - for name, rect in image.excludes.items(): - self.add_rectangle(QRect(*rect), ImageMarking.EXCLUDE, name=name) + image.target_dimension = target_dimension.get(image.crop.size()) + self.add_rectangle(image.crop, ImageMarking.CROP, + size=image.target_dimension) + for marking in image.markings: + self.add_rectangle(marking.rect, marking.type, name=marking.label) @Slot() def setting_change(self, key, value): @@ -655,31 +643,6 @@ def setting_change(self, key, value): marking.size_changed() self.scene.invalidate() - @Slot(QGraphicsRectItem) - def marking_change(self, marking: QGraphicsRectItem): - global grid, base_point - assert self.proxy_image_index != None - assert self.proxy_image_index.isValid() - image: Image = self.proxy_image_index.data(Qt.ItemDataRole.UserRole) - - if marking.rect_type == ImageMarking.CROP: - image.thumbnail = None - image.crop = marking.rect().toRect().getRect() # ensure int! - image.target_dimension = marking.target_size.toTuple() - scale = min(image.crop[2]/image.target_dimension[0], - image.crop[3]/image.target_dimension[1]) - base_point = QPoint(image.crop[0]+floor((image.crop[2]-scale*image.target_dimension[0])/2), - image.crop[1]+floor((image.crop[3]-scale*image.target_dimension[1])/2)) - elif marking.rect_type == ImageMarking.HINT: - image.hints[marking.data(0)] = marking.rect().toRect().getRect() - elif marking.rect_type == ImageMarking.INCLUDE: - image.includes[marking.data(0)] = marking.rect().toRect().getRect() - elif marking.rect_type == ImageMarking.EXCLUDE: - image.excludes[marking.data(0)] = marking.rect().toRect().getRect() - - self.proxy_image_list_model.sourceModel().dataChanged.emit( - self.proxy_image_index, self.proxy_image_index) - @Slot() def zoom_in(self, center_pos: QPoint = None): MarkingItem.zoom_factor = min(MarkingItem.zoom_factor * 1.25, 16) @@ -768,13 +731,36 @@ def add_rectangle(self, rect: QRect, rect_type: ImageMarking, label_background.setPen(Qt.NoPen) marking_item.label = MarkingLabel(name, label_background) marking_item.adjust_layout() - marking_item.signal.change.connect(self.marking_change) + marking_item.signal.change.connect(self.marking_changed) self.scene.addItem(marking_item) self.marking_items.append(marking_item) self.marking.emit(ImageMarking.NONE) if rect_type == ImageMarking.CROP: self.accept_crop_addition.emit(False) - self.marking_change(marking_item) + self.marking_changed(marking_item) + + @Slot(QGraphicsRectItem) + def marking_changed(self, marking: QGraphicsRectItem): + global grid, base_point + assert self.proxy_image_index != None + assert self.proxy_image_index.isValid() + image: Image = self.proxy_image_index.data(Qt.ItemDataRole.UserRole) + + if marking.rect_type == ImageMarking.CROP: + image.thumbnail = None + image.crop = marking.rect().toRect() # ensure int! + image.target_dimension = marking.target_size + scale = min(image.crop.width()/image.target_dimension.width(), + image.crop.height()/image.target_dimension.height()) + base_point = QPoint(image.crop.x()+floor((image.crop.width()-scale*image.target_dimension.width())/2), + image.crop.y()+floor((image.crop.height()-scale*image.target_dimension.height())/2)) + else: + image.markings = [Marking(marking.data(0), + marking.rect_type, + marking.rect().toRect()) + for marking in self.marking_items if marking.rect_type != ImageMarking.CROP] + self.proxy_image_list_model.sourceModel().dataChanged.emit( + self.proxy_image_index, self.proxy_image_index) @Slot() def delete_selected(self): From 265f5a4ce6d084506d2033f58929801b279ad889 Mon Sep 17 00:00:00 2001 From: StableLlama Date: Sun, 2 Mar 2025 22:45:47 +0100 Subject: [PATCH 29/82] Fix litte bugs --- taggui/widgets/image_viewer.py | 62 ++++++++++++++++++++-------------- 1 file changed, 36 insertions(+), 26 deletions(-) diff --git a/taggui/widgets/image_viewer.py b/taggui/widgets/image_viewer.py index a07f24b1..f7fb8b19 100644 --- a/taggui/widgets/image_viewer.py +++ b/taggui/widgets/image_viewer.py @@ -358,7 +358,7 @@ def adjust_layout(self): class MarkingLabel(QGraphicsTextItem): - editingFinished = Signal(str) + editingFinished = Signal() def __init__(self, text, parent): super().__init__(text, parent) @@ -367,14 +367,15 @@ def __init__(self, text, parent): def focusOutEvent(self, event): super().focusOutEvent(event) - self.editingFinished.emit(self.toPlainText()) + self.editingFinished.emit() def keyPressEvent(self, event): - super().keyPressEvent(event) - self.parentItem().setRect(self.sceneBoundingRect()) if event.key() in (Qt.Key_Enter, Qt.Key_Return): self.clearFocus() - self.editingFinished.emit(self.toPlainText()) + self.editingFinished.emit() + else: + super().keyPressEvent(event) + self.parentItem().setRect(self.sceneBoundingRect()) def insertFromMimeData(self, source): if source.hasText(): @@ -520,8 +521,11 @@ def set_insertion_mode(self, mode): else: self.setDragMode(QGraphicsView.DragMode.ScrollHandDrag) self.unsetCursor() - self.scene().removeItem(self.horizontal_line) - self.scene().removeItem(self.vertical_line) + if self.horizontal_line: + self.scene().removeItem(self.horizontal_line) + self.horizontal_line = None + self.scene().removeItem(self.vertical_line) + self.vertical_line = None def update_lines_pos(self): """Show the hint lines at the position self.last_pos. @@ -580,6 +584,7 @@ def keyReleaseEvent(self, event): self.set_insertion_mode(False) super().keyReleaseEvent(event) + class ImageViewer(QWidget): zoom = Signal(float, name='zoomChanged') marking = Signal(ImageMarking, name='markingToAdd') @@ -609,6 +614,7 @@ def __init__(self, proxy_image_list_model: ProxyImageListModel): def load_image(self, proxy_image_index: QModelIndex): self.proxy_image_index = QPersistentModelIndex(proxy_image_index) + self.marking_items.clear() self.view.clear_scene() if not self.proxy_image_index.isValid(): return @@ -706,30 +712,23 @@ def wheelEvent(self, event): def add_rectangle(self, rect: QRect, rect_type: ImageMarking, size: QSize = None, name: str = ''): + self.marking_to_add = ImageMarking.NONE marking_item = MarkingItem(rect, rect_type, size) if rect_type == ImageMarking.CROP: marking_item.signal.move.connect(self.hud_item.setValues) elif name == '' and rect_type != ImageMarking.NONE: image: Image = self.proxy_image_index.data(Qt.ItemDataRole.UserRole) - if rect_type == ImageMarking.HINT: - keys = image.hints.keys() - pre = 'hint' - elif rect_type == ImageMarking.INCLUDE: - keys = image.includes.keys() - pre = 'include' - elif rect_type == ImageMarking.EXCLUDE: - keys = image.excludes.keys() - pre = 'exclude' - count = 1 - while f'{pre}{count}' in keys: - count += 1 - name = f'{pre}{count}' + name = {ImageMarking.HINT: 'hint', + ImageMarking.INCLUDE: 'include', + ImageMarking.EXCLUDE: 'exclude'}[rect_type] + image.markings.append(Marking(name, rect_type, rect)) marking_item.setData(0, name) if rect_type != ImageMarking.CROP and rect_type != ImageMarking.NONE: label_background = QGraphicsRectItem(marking_item) label_background.setBrush(marking_item.color) label_background.setPen(Qt.NoPen) marking_item.label = MarkingLabel(name, label_background) + marking_item.label.editingFinished.connect(self.label_changed) marking_item.adjust_layout() marking_item.signal.change.connect(self.marking_changed) self.scene.addItem(marking_item) @@ -739,6 +738,20 @@ def add_rectangle(self, rect: QRect, rect_type: ImageMarking, self.accept_crop_addition.emit(False) self.marking_changed(marking_item) + @Slot() + def label_changed(self, do_emit = True): + image: Image = self.proxy_image_index.data(Qt.ItemDataRole.UserRole) + image.markings.clear() + for marking in self.marking_items: + if marking.rect_type != ImageMarking.CROP: + marking.label.parentItem().parentItem().setData(0, marking.label.toPlainText()) + image.markings.append(Marking(marking.data(0), + marking.rect_type, + marking.rect().toRect())) + if do_emit: + self.proxy_image_list_model.sourceModel().dataChanged.emit( + self.proxy_image_index, self.proxy_image_index) + @Slot(QGraphicsRectItem) def marking_changed(self, marking: QGraphicsRectItem): global grid, base_point @@ -773,12 +786,9 @@ def delete_selected(self): image.target_dimension = None base_point = QPoint(0, 0) self.accept_crop_addition.emit(True) - elif item.rect_type == ImageMarking.HINT: - del image.hints[item.data(0)] - elif item.rect_type == ImageMarking.INCLUDE: - del image.includes[item.data(0)] - elif item.rect_type == ImageMarking.EXCLUDE: - del image.excludes[item.data(0)] + else: + self.marking_items.remove(item) + self.label_changed(False) self.scene.removeItem(item) self.proxy_image_list_model.sourceModel().dataChanged.emit( self.proxy_image_index, self.proxy_image_index) From 7a70f0e2050de3d8d2e56e738901a7bf9d2cfa1e Mon Sep 17 00:00:00 2001 From: StableLlama Date: Mon, 3 Mar 2025 23:27:45 +0100 Subject: [PATCH 30/82] Add marking edit toolbar buttons --- images/show_label.png | Bin 0 -> 2438 bytes images/show_label.svg | 56 ++++++++++++ images/show_marking.png | Bin 0 -> 3484 bytes images/show_marking.svg | 66 ++++++++++++++ images/toggle_marking.png | Bin 0 -> 12680 bytes images/toggle_marking.svg | 156 +++++++++++++++++++++++++++++++++ taggui/widgets/image_viewer.py | 50 +++++++++-- taggui/widgets/main_window.py | 75 +++++++++++----- 8 files changed, 375 insertions(+), 28 deletions(-) create mode 100644 images/show_label.png create mode 100644 images/show_label.svg create mode 100644 images/show_marking.png create mode 100644 images/show_marking.svg create mode 100644 images/toggle_marking.png create mode 100644 images/toggle_marking.svg diff --git a/images/show_label.png b/images/show_label.png new file mode 100644 index 0000000000000000000000000000000000000000..7adc860331b403b9e3693b14a65d98c8451df1c4 GIT binary patch literal 2438 zcmeAS@N?(olHy`uVBq!ia0y~yU;;9k7&zE~)R&4Yzkn2Hfk$L90|Vb-5N14{zaj-F zC|TkfQ4*Y=R#Ki=l*&+EUaps!mtCBkSdglhUz9%kosASwqp+uoV@SoEw>J-F1_X+@ zUi__UD-q-z)Lt9mC{-}Uszm2TWA2fO&W;y%eokj@vUmKYf;P$TlU=`L&2%5XZGB*eN?Z`aPFr;_TBe#j0ZT67aB7?=#Lq}d57|(Flxex zDr^UPbT8<>%RI01da;asSwF*rDbs;<13S}$Ik)dJGd##IVx)iVs8_~cv0%G#@y==% zhCj!?-({A26p{V*9s`4oA+RnQ@s&3$J^f{x?x#1YaOV3gU>l0T)78&qol`;+0Dzud AL;wH) literal 0 HcmV?d00001 diff --git a/images/show_label.svg b/images/show_label.svg new file mode 100644 index 00000000..79cf035d --- /dev/null +++ b/images/show_label.svg @@ -0,0 +1,56 @@ + + + + + + + + + diff --git a/images/show_marking.png b/images/show_marking.png new file mode 100644 index 0000000000000000000000000000000000000000..e5c8f36658b38c9f2c673c3ecc41289b62b297e0 GIT binary patch literal 3484 zcmd5$6w#Mhx4KjZYHgH|go=s_5>y_WD8?;?R1?_}!vum9)JnmQ z)u>d!1qm+%NHtK11Z=7q2ce<_lqFGXP?jVtApruUkN$JUIyy4iA8+oPb7$^7=e+Ox z&i79E`}sIJxHugCSb$8v?T=;q0l&g!DJV^5`_IjD~1Lyc2<)a+RMCd{C zZktceuBSO{yDYh}X^T|kCrT&sh(el)e(|u$a$Y0HWmFpmxQehW3i)14x_VTriUPXBc8~IJ~05z;`1|_FmRc#x`u4m3qnSSm4i) z`ol!7216@x@?K^_H-7vA0K54B%sB-B-wW+5*8YP8ws7%P0c8>--AF#lX$({`xOmLN zLPy1Y%fvvTG*pN{&^0!iW6YzJ#0c?pQRFp+v4JMM?IdG`aj+jW)z>!U>vkUK?$9-t zTDOZ;C2DpWO_A!%4BLgX%BpJ!nuPx0;T4;&I7eR|>}+YtEj5xaZvs{Jm!v~s@I|#H z-Q9PgRfxkBM?9=!_~5l`jVxGv5_F>|7&j!mJ(_Yh8Yb}b$#`;tgRzfiSXM_U9{ zE+>!nEnVMg(@xLWe+HbJt-CJkDJwP4YzxMbv~_g^!NSc~m{^ALBlQX` zE7-PBh}g#CC3BCJYPQ17KAH*x6yM2X<8VB|-qj-HP$$pEZKsWwYJ#_;*+|~ApsCKI zX+OsRnH^CQKWaHWt>&pjBb#!OSbusyH z^D+4%B8O3K@lix4XK!Zs9BiNeyxR{CKz?P~KMvNp4IpYds8X<1(a8w-M^G@;oj zPg2FlRr?BBb8B|^iIGXNw8|W5R%d7O#~Zo8H!tOUiccs*V3sGVh<)ntUBPg;YrMJM z2R9nQ&<#o0jmZsXGmP@5>khi4stExwEQ+JTJMYtmT2W%hJlZb}CHliM;+NSW3RaFn zZ&e`|!{N$yH=fmBF$l$lEEVtE25WT1!y~S#n?1UEr@R@KuN|%*!H>je?BSj|EW(_tkhDq2bNzok*Jwcds zXVlESw|{#(8oc5XQOoyafxZx-n8-)_4bs2r)ku@4Nq_fbEFFes#(0<}YQqIHgrgVtM`_Ow~vF?(cBzX|-+Dv?B~@iOBp}acXS` z4~*CWjy9p*0{GHrD2P6geSU>DV@WL;E=O<%o=tINrGW?Ff5{8LygpP($Hh^O+W+lt WhxIpJAfkU(VBc^4X@Jx literal 0 HcmV?d00001 diff --git a/images/show_marking.svg b/images/show_marking.svg new file mode 100644 index 00000000..2b55e279 --- /dev/null +++ b/images/show_marking.svg @@ -0,0 +1,66 @@ + + + + + + + + + diff --git a/images/toggle_marking.png b/images/toggle_marking.png new file mode 100644 index 0000000000000000000000000000000000000000..db6e1d7378b8e918d91a9d0fa0fb0c6f8e7d8d59 GIT binary patch literal 12680 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 literal 0 HcmV?d00001 diff --git a/images/toggle_marking.svg b/images/toggle_marking.svg new file mode 100644 index 00000000..29af277a --- /dev/null +++ b/images/toggle_marking.svg @@ -0,0 +1,156 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/taggui/widgets/image_viewer.py b/taggui/widgets/image_viewer.py index f7fb8b19..3d73142a 100644 --- a/taggui/widgets/image_viewer.py +++ b/taggui/widgets/image_viewer.py @@ -22,6 +22,13 @@ # stepsize of the grid grid: int = 8 +marking_colors = { + ImageMarking.CROP: Qt.blue, + ImageMarking.HINT: Qt.gray, + ImageMarking.INCLUDE: Qt.green, + ImageMarking.EXCLUDE: Qt.red, +} + class RectPosition(str, Enum): TL = 'top left' TOP = 'top' @@ -141,12 +148,7 @@ def __init__(self, rect: QRect, rect_type: ImageMarking, self.signal = RectItemSignal() self.rect_type = rect_type self.label: MarkingLabel | None = None - self.color = { - ImageMarking.CROP: Qt.blue, - ImageMarking.HINT: Qt.gray, - ImageMarking.INCLUDE: Qt.green, - ImageMarking.EXCLUDE: Qt.red, - }[rect_type] + self.color = marking_colors[rect_type] if rect_type == ImageMarking.CROP and target_size == None: self.size_changed() # this method sets self.target_size else: @@ -596,6 +598,8 @@ def __init__(self, proxy_image_list_model: ProxyImageListModel): MarkingItem.pen_half_width = round(self.devicePixelRatio()) MarkingItem.zoom_factor = 1.0 self.is_zoom_to_fit = True + self.show_marking_state = True + self.show_label_state = True self.marking_to_add = ImageMarking.NONE self.scene = QGraphicsScene() self.view = ImageGraphicsView(self.scene, self) @@ -698,6 +702,32 @@ def add_marking(self, marking: ImageMarking): self.view.set_insertion_mode(marking != ImageMarking.NONE) grid = 1 if marking == ImageMarking.CROP else 8 + @Slot() + def toggle_marking(self): + for selected in self.scene.selectedItems(): + selected.rect_type = {ImageMarking.HINT: ImageMarking.EXCLUDE, + ImageMarking.INCLUDE: ImageMarking.HINT, + ImageMarking.EXCLUDE: ImageMarking.INCLUDE + }[selected.rect_type] + selected.color = marking_colors[selected.rect_type] + selected.label.parentItem().setBrush(selected.color) + self.marking_changed(selected) + selected.update() + + @Slot(bool) + def show_marking(self, checked: bool): + self.show_marking_state = checked + for marking in self.marking_items: + marking.setVisible(checked) + + @Slot(bool) + def show_label(self, checked: bool): + self.show_label_state = checked + for marking in self.marking_items: + if marking.label: + marking.label.setVisible(checked) + marking.label.parentItem().setVisible(checked) + def wheelEvent(self, event): old_pos = self.view.mapToScene(event.position().toPoint()) @@ -714,6 +744,7 @@ def add_rectangle(self, rect: QRect, rect_type: ImageMarking, size: QSize = None, name: str = ''): self.marking_to_add = ImageMarking.NONE marking_item = MarkingItem(rect, rect_type, size) + marking_item.setVisible(self.show_marking_state) if rect_type == ImageMarking.CROP: marking_item.signal.move.connect(self.hud_item.setValues) elif name == '' and rect_type != ImageMarking.NONE: @@ -727,7 +758,9 @@ def add_rectangle(self, rect: QRect, rect_type: ImageMarking, label_background = QGraphicsRectItem(marking_item) label_background.setBrush(marking_item.color) label_background.setPen(Qt.NoPen) + label_background.setVisible(self.show_label_state) marking_item.label = MarkingLabel(name, label_background) + marking_item.label.setVisible(self.show_label_state) marking_item.label.editingFinished.connect(self.label_changed) marking_item.adjust_layout() marking_item.signal.change.connect(self.marking_changed) @@ -775,6 +808,11 @@ def marking_changed(self, marking: QGraphicsRectItem): self.proxy_image_list_model.sourceModel().dataChanged.emit( self.proxy_image_index, self.proxy_image_index) + def get_selected_type(self) -> ImageMarking: + if len(self.scene.selectedItems()) > 0: + return self.scene.selectedItems()[0].rect_type + return ImageMarking.NONE + @Slot() def delete_selected(self): image: Image = self.proxy_image_index.data(Qt.ItemDataRole.UserRole) diff --git a/taggui/widgets/main_window.py b/taggui/widgets/main_window.py index 7a82fc4c..820feef9 100644 --- a/taggui/widgets/main_window.py +++ b/taggui/widgets/main_window.py @@ -32,6 +32,9 @@ from widgets.image_viewer import ImageViewer, ImageMarking ICON_PATH = Path('images/icon.ico') +TOGGLE_MARKING_ICON_PATH = Path('images/toggle_marking.png') +SHOW_MARKINGS_ICON_PATH = Path('images/show_marking.png') +SHOW_LABELS_ICON_PATH = Path('images/show_label.png') GITHUB_REPOSITORY_URL = 'https://github.com/jhc13/taggui' TOKENIZER_DIRECTORY_PATH = Path('clip-vit-base-patch32') @@ -43,6 +46,8 @@ def __init__(self, app: QApplication): # The path of the currently loaded directory. This is set later when a # directory is loaded. self.directory_path = None + self.is_running = True + app.aboutToQuit.connect(lambda: setattr(self, 'is_running', False)) image_list_image_width = settings.value( 'image_list_image_width', defaultValue=DEFAULT_SETTINGS['image_list_image_width'], type=int) @@ -65,7 +70,6 @@ def __init__(self, app: QApplication): # everything has the correct font size. self.set_font_size() self.image_viewer = ImageViewer(self.proxy_image_list_model) - self.image_viewer.zoom.connect(self.zoom) self.create_central_widget() self.toolbar = QToolBar('Main toolbar', self) @@ -97,30 +101,35 @@ def __init__(self, app: QApplication): 'Add hint', self.add_action_group) self.add_hint_action.setCheckable(True) self.toolbar.addAction(self.add_hint_action) - self.add_include_action = QAction(create_add_box_icon(Qt.green), - 'Add include mask', self.add_action_group) - self.add_include_action.setCheckable(True) - self.toolbar.addAction(self.add_include_action) self.add_exclude_action = QAction(create_add_box_icon(Qt.red), 'Add exclude mask', self.add_action_group) self.add_exclude_action.setCheckable(True) self.toolbar.addAction(self.add_exclude_action) - self.image_viewer.marking.connect(lambda marking: - self.add_crop_action.setChecked(True) if marking == ImageMarking.CROP else - self.add_hint_action.setChecked(True) if marking == ImageMarking.HINT else - self.add_include_action.setChecked(True) if marking == ImageMarking.INCLUDE else - self.add_exclude_action.setChecked(True) if marking == ImageMarking.EXCLUDE else - self.add_action_group.checkedAction() and - self.add_action_group.checkedAction().setChecked(False)) - self.image_viewer.accept_crop_addition.connect(self.add_crop_action.setEnabled) + self.add_include_action = QAction(create_add_box_icon(Qt.green), + 'Add include mask', self.add_action_group) + self.add_include_action.setCheckable(True) + self.toolbar.addAction(self.add_include_action) self.delete_marking_action = QAction(QIcon.fromTheme('edit-delete'), 'Delete marking', self) self.delete_marking_action.setEnabled(False) self.toolbar.addAction(self.delete_marking_action) - self.image_viewer.scene.selectionChanged.connect(lambda: - self.image_viewer.scene and self.delete_marking_action.setEnabled( - len(self.image_viewer.scene.selectedItems())>0)) - + self.add_toggle_marking_action = QAction( + QIcon(QPixmap(get_resource_path(TOGGLE_MARKING_ICON_PATH))), + 'Change marking type', self) + self.add_toggle_marking_action.setEnabled(False) + self.toolbar.addAction(self.add_toggle_marking_action) + self.add_show_marking_action = QAction( + QIcon(QPixmap(get_resource_path(SHOW_MARKINGS_ICON_PATH))), + 'Show markings', self) + self.add_show_marking_action.setCheckable(True) + self.add_show_marking_action.setChecked(True) + self.toolbar.addAction(self.add_show_marking_action) + self.add_show_labels_action = QAction( + QIcon(QPixmap(get_resource_path(SHOW_LABELS_ICON_PATH))), + 'Show labels', self) + self.add_show_labels_action.setCheckable(True) + self.add_show_labels_action.setChecked(True) + self.toolbar.addAction(self.add_show_labels_action) self.image_list = ImageList(self.proxy_image_list_model, tag_separator, image_list_image_width) @@ -512,6 +521,10 @@ def save_image_index(self, proxy_image_index: QModelIndex): settings.setValue(settings_key, proxy_image_index.row()) def connect_toolbar_signals(self): + self.toolbar.visibilityChanged.connect( + lambda: self.toggle_toolbar_action.setChecked( + self.toolbar.isVisible())) + self.image_viewer.zoom.connect(self.zoom) self.zoom_fit_best_action.triggered.connect( self.image_viewer.zoom_fit) self.zoom_in_action.triggered.connect( @@ -520,17 +533,35 @@ def connect_toolbar_signals(self): self.image_viewer.zoom_original) self.zoom_out_action.triggered.connect( self.image_viewer.zoom_out) - self.toolbar.visibilityChanged.connect( - lambda: self.toggle_toolbar_action.setChecked( - self.toolbar.isVisible())) self.add_action_group.triggered.connect( lambda action: self.image_viewer.add_marking( ImageMarking.NONE if not action.isChecked() else ImageMarking.CROP if action == self.add_crop_action else ImageMarking.HINT if action == self.add_hint_action else - ImageMarking.INCLUDE if action == self.add_include_action else - ImageMarking.EXCLUDE)) + ImageMarking.EXCLUDE if action == self.add_exclude_action else + ImageMarking.INCLUDE)) + self.image_viewer.marking.connect(lambda marking: + self.add_crop_action.setChecked(True) if marking == ImageMarking.CROP else + self.add_hint_action.setChecked(True) if marking == ImageMarking.HINT else + self.add_exclude_action.setChecked(True) if marking == ImageMarking.EXCLUDE else + self.add_include_action.setChecked(True) if marking == ImageMarking.INCLUDE else + self.add_action_group.checkedAction() and + self.add_action_group.checkedAction().setChecked(False)) + self.image_viewer.scene.selectionChanged.connect(lambda: + self.is_running and self.add_toggle_marking_action.setEnabled( + self.image_viewer.get_selected_type() not in [ImageMarking.NONE, + ImageMarking.CROP])) + self.image_viewer.accept_crop_addition.connect(self.add_crop_action.setEnabled) + self.image_viewer.scene.selectionChanged.connect(lambda: + self.is_running and self.delete_marking_action.setEnabled( + self.image_viewer.get_selected_type() != ImageMarking.NONE)) self.delete_marking_action.triggered.connect(self.image_viewer.delete_selected) + self.add_show_marking_action.toggled.connect(self.image_viewer.show_marking) + self.add_show_marking_action.toggled.connect(self.add_action_group.setEnabled) + self.add_show_marking_action.toggled.connect(self.add_toggle_marking_action.setEnabled) + self.add_show_marking_action.toggled.connect(self.add_show_labels_action.setEnabled) + self.add_toggle_marking_action.triggered.connect(self.image_viewer.toggle_marking) + self.add_show_labels_action.toggled.connect(self.image_viewer.show_label) def connect_image_list_signals(self): self.image_list.filter_line_edit.textChanged.connect( From f082e8d91a484bc79d5db4a3ad700d93081a1e4f Mon Sep 17 00:00:00 2001 From: StableLlama Date: Wed, 5 Mar 2025 17:35:25 +0100 Subject: [PATCH 31/82] Add context menu for markings --- taggui/widgets/image_viewer.py | 83 ++++++++++++++++++++++++++++------ taggui/widgets/main_window.py | 4 +- 2 files changed, 70 insertions(+), 17 deletions(-) diff --git a/taggui/widgets/image_viewer.py b/taggui/widgets/image_viewer.py index 3d73142a..ad25c226 100644 --- a/taggui/widgets/image_viewer.py +++ b/taggui/widgets/image_viewer.py @@ -4,12 +4,12 @@ from PySide6.QtCore import (QEvent, QModelIndex, QObject, QPersistentModelIndex, QPoint, QPointF, QRect, QRectF, QSize, QSizeF, Qt, Signal, Slot) -from PySide6.QtGui import (QCursor, QColor, QPainter, QPainterPath, QPen, - QPixmap, QTransform) +from PySide6.QtGui import (QAction, QActionGroup, QCursor, QColor, QIcon, + QPainter, QPainterPath, QPen, QPixmap, QTransform) from PySide6.QtWidgets import (QGraphicsItem, QGraphicsLineItem, QGraphicsPixmapItem, QGraphicsRectItem, QGraphicsTextItem, QGraphicsScene, QGraphicsView, - QVBoxLayout, QWidget) + QMenu, QVBoxLayout, QWidget) from utils.settings import settings from models.proxy_image_list_model import ProxyImageListModel from utils.image import Image, ImageMarking, Marking @@ -200,7 +200,8 @@ def hoverMoveEvent(self, event): def mousePressEvent(self, event): self.handle_selected = self.handleAt(event.pos()) - if self.handle_selected != RectPosition.NONE: + if (event.button() == Qt.MouseButton.LeftButton and + self.handle_selected != RectPosition.NONE): self.mouse_press_pos = event.pos() self.mouse_press_scene_pos = event.scenePos() self.mouse_press_rect = self.rect() @@ -491,6 +492,8 @@ def paint(self, painter, option, widget=None): class ImageGraphicsView(QGraphicsView): def __init__(self, scene, image_viewer): super().__init__(scene) + self.setContextMenuPolicy(Qt.CustomContextMenu) + self.customContextMenuRequested.connect(self.showContextMenu) self.setRenderHint(QPainter.Antialiasing) self.setDragMode(QGraphicsView.DragMode.ScrollHandDrag) self.setTransformationAnchor(QGraphicsView.ViewportAnchor.AnchorUnderMouse) @@ -500,6 +503,43 @@ def __init__(self, scene, image_viewer): self.last_pos = None self.clear_scene() + def showContextMenu(self, pos): + scene_pos = self.mapToScene(pos) + item = self.scene().itemAt(scene_pos, self.transform()) + if item is not None and item.handle_selected != RectPosition.NONE: + menu = QMenu() + if isinstance(item, MarkingLabel): + item = item.parentItem().parentItem() + if isinstance(item, MarkingItem): + if item.rect_type != ImageMarking.NONE: + if item.rect_type != ImageMarking.CROP: + marking_group = QActionGroup(menu) + change_to_hint_action = QAction('Hint', marking_group) + change_to_hint_action.setCheckable(True) + change_to_hint_action.setChecked(item.rect_type == ImageMarking.HINT) + change_to_hint_action.triggered.connect( + lambda: self.image_viewer.change_marking([item], ImageMarking.HINT)) + menu.addAction(change_to_hint_action) + change_to_exclude_action = QAction('Exclude', marking_group) + change_to_exclude_action.setCheckable(True) + change_to_exclude_action.setChecked(item.rect_type == ImageMarking.EXCLUDE) + change_to_exclude_action.triggered.connect( + lambda: self.image_viewer.change_marking([item], ImageMarking.EXCLUDE)) + menu.addAction(change_to_exclude_action) + change_to_include_action = QAction('Include', marking_group) + change_to_include_action.setCheckable(True) + change_to_include_action.setChecked(item.rect_type == ImageMarking.INCLUDE) + change_to_include_action.triggered.connect( + lambda: self.image_viewer.change_marking([item], ImageMarking.INCLUDE)) + menu.addAction(change_to_include_action) + menu.addSeparator() + delete_marking_action = QAction( + QIcon.fromTheme('edit-delete'), 'Delete', self) + delete_marking_action.triggered.connect( + lambda: self.image_viewer.delete_markings([item])) + menu.addAction(delete_marking_action) + menu.exec(self.mapToGlobal(pos)) + def clear_scene(self): """Use this and not scene.clear() due to resource management.""" global grid, base_point @@ -578,7 +618,11 @@ def keyPressEvent(self, event): if event.key() == Qt.Key.Key_Control: self.set_insertion_mode(True) elif event.key() == Qt.Key.Key_Delete: - self.image_viewer.delete_selected() + edited_item = self.scene().focusItem() + if not (isinstance(edited_item, MarkingLabel) and + edited_item.textInteractionFlags() == Qt.TextEditorInteraction): + # Delete marking only when not editing the label + self.image_viewer.delete_markings() super().keyPressEvent(event) def keyReleaseEvent(self, event): @@ -703,16 +747,23 @@ def add_marking(self, marking: ImageMarking): grid = 1 if marking == ImageMarking.CROP else 8 @Slot() - def toggle_marking(self): - for selected in self.scene.selectedItems(): - selected.rect_type = {ImageMarking.HINT: ImageMarking.EXCLUDE, + def change_marking(self, items: list[MarkingItem] | None = None, + new_marking: ImageMarking = ImageMarking.NONE): + if items == None: + items = self.scene.selectedItems() + for item in items: + if new_marking == ImageMarking.NONE: + # default: toggle between all types + item.rect_type = {ImageMarking.HINT: ImageMarking.EXCLUDE, ImageMarking.INCLUDE: ImageMarking.HINT, ImageMarking.EXCLUDE: ImageMarking.INCLUDE - }[selected.rect_type] - selected.color = marking_colors[selected.rect_type] - selected.label.parentItem().setBrush(selected.color) - self.marking_changed(selected) - selected.update() + }[item.rect_type] + else: + item.rect_type = new_marking + item.color = marking_colors[item.rect_type] + item.label.parentItem().setBrush(item.color) + self.marking_changed(item) + item.update() @Slot(bool) def show_marking(self, checked: bool): @@ -814,9 +865,11 @@ def get_selected_type(self) -> ImageMarking: return ImageMarking.NONE @Slot() - def delete_selected(self): + def delete_markings(self, items: list[MarkingItem] | None = None): image: Image = self.proxy_image_index.data(Qt.ItemDataRole.UserRole) - for item in self.scene.selectedItems(): + if items == None: + items = self.scene.selectedItems() + for item in items: if item.rect_type == ImageMarking.CROP: global base_point image.thumbnail = None diff --git a/taggui/widgets/main_window.py b/taggui/widgets/main_window.py index 820feef9..e983996b 100644 --- a/taggui/widgets/main_window.py +++ b/taggui/widgets/main_window.py @@ -555,12 +555,12 @@ def connect_toolbar_signals(self): self.image_viewer.scene.selectionChanged.connect(lambda: self.is_running and self.delete_marking_action.setEnabled( self.image_viewer.get_selected_type() != ImageMarking.NONE)) - self.delete_marking_action.triggered.connect(self.image_viewer.delete_selected) + self.delete_marking_action.triggered.connect(lambda: self.image_viewer.delete_markings()) self.add_show_marking_action.toggled.connect(self.image_viewer.show_marking) self.add_show_marking_action.toggled.connect(self.add_action_group.setEnabled) self.add_show_marking_action.toggled.connect(self.add_toggle_marking_action.setEnabled) self.add_show_marking_action.toggled.connect(self.add_show_labels_action.setEnabled) - self.add_toggle_marking_action.triggered.connect(self.image_viewer.toggle_marking) + self.add_toggle_marking_action.triggered.connect(self.image_viewer.change_marking) self.add_show_labels_action.toggled.connect(self.image_viewer.show_label) def connect_image_list_signals(self): From fd4d6d4a949127fe783fdf4e11a931febb6a2739 Mon Sep 17 00:00:00 2001 From: StableLlama Date: Wed, 5 Mar 2025 17:56:28 +0100 Subject: [PATCH 32/82] All filtering for marking labels --- README.md | 2 ++ taggui/models/proxy_image_list_model.py | 2 ++ taggui/widgets/image_list.py | 3 ++- 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 19ae0fc1..880528f0 100644 --- a/README.md +++ b/README.md @@ -138,6 +138,8 @@ apply: - `caption:cat` will match images that have `cat` anywhere in the caption. For example, images with the tag `orange cat` or the tag `catastrophe`. +- `marking`: Images that contain at least one marking with this label. It + doesn't matther whether it is a hint, include or exclude marking. - `name`: Images that contain the filter term in the file name - `name:cat` will match images such as `cat-1.jpg` or `large_cat.png`. - `path`: Images that contain the filter term in the full file path diff --git a/taggui/models/proxy_image_list_model.py b/taggui/models/proxy_image_list_model.py index 25e050c9..0961b424 100644 --- a/taggui/models/proxy_image_list_model.py +++ b/taggui/models/proxy_image_list_model.py @@ -33,6 +33,8 @@ def does_image_match_filter(self, image: Image, if filter_[0] == 'caption': caption = self.tag_separator.join(image.tags) return fnmatchcase(caption, f'*{filter_[1]}*') + if filter_[0] == 'marking': + return any(fnmatchcase(markings.label, filter_[1]) for markings in image.markings) if filter_[0] == 'name': return fnmatchcase(image.path.name, f'*{filter_[1]}*') if filter_[0] == 'path': diff --git a/taggui/widgets/image_list.py b/taggui/widgets/image_list.py index f51fdbab..74212549 100644 --- a/taggui/widgets/image_list.py +++ b/taggui/widgets/image_list.py @@ -48,7 +48,8 @@ def __init__(self): | QuotedString(quote_char="'", esc_char='\\') | Word(printables, exclude_chars='()')) - string_filter_keys = ['tag', 'caption', 'name', 'path', 'size', 'target'] + string_filter_keys = ['tag', 'caption', 'marking', 'name', 'path', + 'size', 'target'] string_filter_expressions = [Group(CaselessLiteral(key) + Suppress(':') + optionally_quoted_string) for key in string_filter_keys] From f9c1a6c551b5f4d925010645f1674d449bc09bc8 Mon Sep 17 00:00:00 2001 From: StableLlama Date: Thu, 6 Mar 2025 23:20:13 +0100 Subject: [PATCH 33/82] Implement export of croped images Also handle includes and excludes. Allow filtering of tags with hashtags. Little bug fixing. --- README.md | 8 ++ taggui/dialogs/export_dialog.py | 149 +++++++++++++++++++++++++------- taggui/utils/settings.py | 3 +- taggui/widgets/image_viewer.py | 3 +- 4 files changed, 131 insertions(+), 32 deletions(-) diff --git a/README.md b/README.md index 880528f0..925a730c 100644 --- a/README.md +++ b/README.md @@ -323,3 +323,11 @@ be used for the exported images as well when selected. `Statistics`: Preview of the generated image sizes from the export funtion. + +`Fiter hashtag (#) tags`: +When set all tags that start with a hashtag (i.e. the "#" character) are not +included in the exported captions. + +This allows you to use tags for internal image organization like marking +images that you don't want to export or adding notes about an image into the +tag list. diff --git a/taggui/dialogs/export_dialog.py b/taggui/dialogs/export_dialog.py index 9ffab2e4..0ef537e2 100644 --- a/taggui/dialogs/export_dialog.py +++ b/taggui/dialogs/export_dialog.py @@ -16,6 +16,7 @@ from PIL import Image, ImageFilter, ImageCms from utils.settings import DEFAULT_SETTINGS, settings +from utils.image import ImageMarking from utils.settings_widgets import (SettingsBigCheckBox, SettingsLineEdit, SettingsSpinBox, SettingsComboBox) import utils.target_dimension as target_dimension @@ -112,8 +113,8 @@ def __init__(self, parent, image_list: ImageList): '0: disable rescaling\n' '512: SD1.5\n' '1024: SDXL, SD3, Flux') - self.resolution_spin_box.textChanged.connect(self.show_megapixels) - self.resolution_spin_box.textChanged.connect(self.show_statistics) + self.resolution_spin_box.valueChanged.connect(self.show_megapixels) + self.resolution_spin_box.valueChanged.connect(self.show_statistics) grid_layout.addWidget(self.resolution_spin_box, grid_row, 1, Qt.AlignmentFlag.AlignLeft) @@ -134,7 +135,7 @@ def __init__(self, parent, image_list: ImageList): 'Ensure that the exported image size is divisable by that number.\n' 'It should match the setting on the training tool.\n' 'It might cause minor cropping.') - self.bucket_res_size_spin_box.textChanged.connect(self.show_statistics) + self.bucket_res_size_spin_box.valueChanged.connect(self.show_statistics) grid_layout.addWidget(self.bucket_res_size_spin_box, grid_row, 1, Qt.AlignmentFlag.AlignLeft) @@ -199,7 +200,7 @@ def __init__(self, parent, image_list: ImageList): 'Only for JPEG and WebP.\n' '0 is worst and 100 is best.\n' 'For JPEG numbers above 95 should be avoided') - self.quality_spin_box.textChanged.connect(self.quality_change) + self.quality_spin_box.valueChanged.connect(self.quality_change) format_layout.addWidget(self.quality_spin_box, Qt.AlignmentFlag.AlignLeft) format_widget.setLayout(format_layout) @@ -266,6 +267,15 @@ def __init__(self, parent, image_list: ImageList): grid_layout.addWidget(self.statistics_table, grid_row, 1, Qt.AlignmentFlag.AlignLeft) + grid_row += 1 + grid_layout.addWidget(QLabel('Fiter hashtag (#) tags'), grid_row, 0, + Qt.AlignmentFlag.AlignRight) + self.filter_hastag_check_box = SettingsBigCheckBox(key='export_filter_hashtag') + self.filter_hastag_check_box.setToolTip( + 'Do not export tags that start with a hashtag (#)') + grid_layout.addWidget(self.filter_hastag_check_box, grid_row, 1, + Qt.AlignmentFlag.AlignLeft) + self.layout.addLayout(grid_layout) self.export_button = QPushButton('Export') @@ -350,7 +360,6 @@ def show_statistics(self): return resolution = settings.value('export_resolution', type=int) - upscaling = settings.value('export_upscaling', type=int) bucket_res = settings.value('export_bucket_res_size', type=int) # notable aspect ratios @@ -367,8 +376,12 @@ def show_statistics(self): image_list = self.get_image_list() image_dimensions = defaultdict(int) for this_image in image_list: - this_image.target_dimension = target_dimension.get( - QSize(*this_image.dimensions)) + if this_image.crop != None: + this_image.target_dimension = target_dimension.get( + this_image.crop.size()) + else: + this_image.target_dimension = target_dimension.get( + QSize(*this_image.dimensions)) image_dimensions[this_image.target_dimension.toTuple()] += 1 self.image_list.proxy_image_list_model.invalidate() @@ -442,6 +455,7 @@ def do_export(self): export_keep_dir_structure = settings.value('export_keep_dir_structure', type=bool) no_overwrite = True only_missing = True + refresh_tags = False image_list = self.get_image_list() self.progress_bar = QProgressBar(self) @@ -462,14 +476,22 @@ def do_export(self): msgBox.setIcon(QMessageBox.Warning) msgBox.setWindowTitle('Path warning') msgBox.setText('The export directory path is not empty') + refresh_button = msgBox.addButton('Refresh', QMessageBox.ApplyRole) + refresh_button.setToolTip('Export only missing images, but update all captions') overwrite_button = msgBox.addButton('Overwrite', QMessageBox.DestructiveRole) + refresh_button.setToolTip('Overwrite all existing files') rename_button = msgBox.addButton('Rename', QMessageBox.YesRole) + refresh_button.setToolTip('Export with a new name') only_missing_button = msgBox.addButton('Only missing', QMessageBox.AcceptRole) + refresh_button.setToolTip('Export only missing images') msgBox.addButton(QMessageBox.Cancel) - msgBox.setDefaultButton(QMessageBox.Cancel) + msgBox.setDefaultButton(refresh_button) button = msgBox.exec_() if button == QMessageBox.Cancel: return + if msgBox.clickedButton() == refresh_button: + no_overwrite = False + refresh_tags = True if msgBox.clickedButton() == overwrite_button: no_overwrite = False only_missing = False @@ -483,8 +505,9 @@ def do_export(self): ) return + tag_separator = settings.value('tag_separator', type=str) + filter_hashtag = settings.value('export_filter_hashtag', type=bool) resolution = settings.value('export_resolution', type=int) - upscaling = settings.value('export_upscaling', type=int) bucket_res = settings.value('export_bucket_res_size', type=int) export_format = settings.value('export_format', type=str) quality = settings.value('export_quality', type=int) @@ -505,55 +528,115 @@ def do_export(self): export_path = export_directory_path / image_entry.path.name export_path = export_path.with_suffix(export_format.split(' ', 1)[0]) - if export_path.exists() and only_missing: + image_exists = export_path.exists() + if image_exists and only_missing and not refresh_tags: continue if no_overwrite: stem = export_path.stem counter = 0 - while export_path.exists(): + while image_exists: export_path = export_path.parent / f"{stem}_{counter}{export_path.suffix}" + image_exists = export_path.exists() counter += 1 - # copy the tag file first - if image_entry.path.with_suffix('.txt').exists(): - shutil.copyfile(str(image_entry.path.with_suffix('.txt')), str(export_path.with_suffix('.txt'))) + # write the tag file first + if filter_hashtag: + tags = [tag for tag in image_entry.tags if tag[0] != '#'] + else: + tags = image_entry.tags + + try: + export_path.with_suffix('.txt').write_text( + tag_separator.join(tags), encoding='utf-8', + errors='replace') + except OSError: + error_message_box = QMessageBox() + error_message_box.setWindowTitle('Error') + error_message_box.setIcon(QMessageBox.Icon.Critical) + error_message_box.setText(f'Failed to save tags for {image_entry.path}.') + error_message_box.exec() + + if image_exists and only_missing: + # tags were refreshed, export_path was changed when we should + # rename and not overwrite, so we can skip the image writing + continue # then handle the image image_file = Image.open(image_entry.path) + export_can_alpha = export_format != ExportFormat.JPG # Preserve alpha if present: - if image_file.mode in ("RGBA", "LA", "PA") and not export_format == ExportFormat.JPG: # Check for alpha channels + if image_file.mode in ("RGBA", "LA", "PA") and export_can_alpha: # Check for alpha channels image_file = image_file.convert("RGBA") else: image_file = image_file.convert("RGB") # Otherwise, convert to RGB + current_width, current_height = image_file.size + + # 1. pass: add includes + for marking in image_entry.markings: + if marking.type == ImageMarking.INCLUDE and export_can_alpha: + if image_file.mode == 'RGB': + image_file = image_file.convert("RGBA") + alpha = image_file.getchannel('A') + # completely transparent + alpha.paste(0, (0, 0, current_width, current_height)) + else: + alpha = image_file.getchannel('A') + alpha.paste(255, marking.rect.adjusted(0,0,1,1).getCoords()) + image_file.putalpha(alpha) + + # 2. pass: remove excludes + for marking in image_entry.markings: + if marking.type == ImageMarking.EXCLUDE and export_can_alpha: + if image_file.mode == 'RGB': + image_file = image_file.convert("RGBA") + alpha = image_file.getchannel('A') + # completely opaque + alpha.paste(255, (0, 0, current_width, current_height)) + else: + alpha = image_file.getchannel('A') + alpha.paste(0, marking.rect.adjusted(0,0,1,1).getCoords()) + image_file.putalpha(alpha) new_width, new_height = image_entry.target_dimension.toTuple() - current_width, current_height = image_file.size - if bucket_strategy == BucketStrategy.CROP or bucket_strategy == BucketStrategy.CROP_SCALE: - if current_height * new_width / current_width < new_height: # too wide - new_width = floor(current_width * new_height / current_height) + crop_width, crop_height = image_entry.crop.size().toTuple() + if (bucket_strategy == BucketStrategy.CROP or + bucket_strategy == BucketStrategy.CROP_SCALE): + if crop_height * new_width / crop_width < new_height: # too wide + new_width = floor(crop_width * new_height / crop_height) else: # too high - new_height = floor(current_height * new_width / current_width) + new_height = floor(crop_height * new_width / crop_width) if bucket_strategy == BucketStrategy.CROP_SCALE: new_width = floor((image_entry.target_dimension.width() + new_width)/2) new_height = floor((image_entry.target_dimension.height() + new_height)/2) - if image_file.size[0] != new_width or image_file.size[1] != new_height: + + if image_entry.crop == None: + cropped_image = image_file + else: + cropped_image = image_file.crop(image_entry.crop.adjusted(0,0,1,1).getCoords()) + + if cropped_image.size[0] != new_width or cropped_image.size[1] != new_height: # resize with the best method available - resized_image = image_file.resize((new_width, new_height), Image.LANCZOS) + resized_image = cropped_image.resize((new_width, new_height), Image.LANCZOS) # followed by a slight sharpening as it should be done - sharpend_image = resized_image.filter(ImageFilter.UnsharpMask(radius = 0.5, percent = 50, threshold = 0)) + sharpend_image = resized_image.filter( + ImageFilter.UnsharpMask(radius = 0.5, percent = 50, threshold = 0)) else: - sharpend_image = image_file + sharpend_image = cropped_image # crop to the desired size current_width, current_height = sharpend_image.size crop_width = floor((current_width - image_entry.target_dimension.width()) / 2) crop_height = floor((current_height - image_entry.target_dimension.height()) / 2) - cropped_image = sharpend_image.crop((crop_width, crop_height, current_width - crop_width, current_height - crop_height)) + cropped_image = sharpend_image.crop((crop_width, crop_height, + crop_width + image_entry.target_dimension.width(), + crop_height + image_entry.target_dimension.height())) lossless = quality > 99 if color_space == "feed through (don't touch)": - cropped_image.save(export_path, format=ExportFormatDict[export_format], quality=quality, icc_profile=cropped_image.info.get('icc_profile'), lossless=lossless) + cropped_image.save(export_path, format=ExportFormatDict[export_format], + quality=quality, lossless=lossless, + icc_profile=cropped_image.info.get('icc_profile') ) else: source_profile_raw = image_file.info.get('icc_profile') if source_profile_raw is None: # assume sRGB @@ -563,9 +646,13 @@ def do_export(self): target_profile = ImageCms.ImageCmsProfile(io.BytesIO(target_profile_raw)) final_image = ImageCms.profileToProfile(cropped_image, source_profile, target_profile) if save_profile: - final_image.save(export_path, format=ExportFormatDict[export_format], quality=quality, icc_profile=target_profile.tobytes(), lossless=lossless) + final_image.save(export_path, format=ExportFormatDict[export_format], + quality=quality, lossless=lossless, + icc_profile=target_profile.tobytes()) else: - final_image.save(export_path, format=ExportFormatDict[export_format], quality=quality, icc_profile=None, lossless=lossless) + final_image.save(export_path, format=ExportFormatDict[export_format], + quality=quality, lossless=lossless, + icc_profile=None) self.close() def get_image_list(self): @@ -580,9 +667,11 @@ def get_image_list(self): image_list.append(source_index.data(Qt.ItemDataRole.UserRole)) elif settings.value('export_filter') == ExportFilter.SELECTED: images = image_list_view.proxy_image_list_model.sourceModel() - image_list = [image_index.data(Qt.ItemDataRole.UserRole) for image_index in image_list_view.get_selected_image_indices()] + image_list = [image_index.data(Qt.ItemDataRole.UserRole) + for image_index in image_list_view.get_selected_image_indices()] else: # ExportFilter.NONE images = image_list_view.proxy_image_list_model.sourceModel() - image_list = [images.index(image_index).data(Qt.ItemDataRole.UserRole) for image_index in range(images.rowCount())] + image_list = [images.index(image_index).data(Qt.ItemDataRole.UserRole) + for image_index in range(images.rowCount())] return image_list diff --git a/taggui/utils/settings.py b/taggui/utils/settings.py index 19dce534..9d5ac600 100644 --- a/taggui/utils/settings.py +++ b/taggui/utils/settings.py @@ -21,7 +21,8 @@ 'export_quality': 75, 'export_color_space': 'sRGB', 'export_directory_path': '', - 'export_keep_dir_structure': False + 'export_keep_dir_structure': False, + 'export_filter_hashtag': True } diff --git a/taggui/widgets/image_viewer.py b/taggui/widgets/image_viewer.py index ad25c226..fcbf0442 100644 --- a/taggui/widgets/image_viewer.py +++ b/taggui/widgets/image_viewer.py @@ -692,7 +692,8 @@ def load_image(self, proxy_image_index: QModelIndex): @Slot() def setting_change(self, key, value): - if key == 'export_bucket_strategy': + if key in ['export_resolution', 'export_bucket_res_size', + 'export_upscaling', 'export_bucket_strategy']: for marking in self.marking_items: marking.size_changed() self.scene.invalidate() From fcc2bc56cbf181cc77812636bd1cffa0a83523ae Mon Sep 17 00:00:00 2001 From: StableLlama Date: Sun, 9 Mar 2025 22:04:17 +0100 Subject: [PATCH 34/82] Coordinate work for display and export Option for latent size and displaying its effect on the markings Allow to create export directory when it's not existing Unify export size calculation --- images/show_marking_latent.png | Bin 0 -> 2796 bytes images/show_marking_latent.svg | 66 +++++++++ taggui/dialogs/export_dialog.py | 237 ++++++++++++++++-------------- taggui/models/image_list_model.py | 8 +- taggui/utils/enums.py | 40 +++++ taggui/utils/grid.py | 90 ++++++++++++ taggui/utils/settings.py | 2 + taggui/widgets/image_viewer.py | 145 +++++++++--------- taggui/widgets/main_window.py | 15 +- 9 files changed, 411 insertions(+), 192 deletions(-) create mode 100644 images/show_marking_latent.png create mode 100644 images/show_marking_latent.svg create mode 100644 taggui/utils/grid.py diff --git a/images/show_marking_latent.png b/images/show_marking_latent.png new file mode 100644 index 0000000000000000000000000000000000000000..0d17b32223b82e0c122158dd7bd68aaa413b3434 GIT binary patch literal 2796 zcmeAS@N?(olHy`uVBq!ia0y~yU;;9k7&zE~)R&4Yzkn2Hfk$L90|Vb-5N14{zaj-F zC|TkfQ4*Y=R#Ki=l*&+EUaps!mtCBkSdglhUz9%kosAR&16QS|i(^Q|oVU03ul`ml zd;H_~(&|d%n${BLupyQ6af4G%i=od^{aCD)=63+>XGDMtM zzg}Y#>=ZcqQ0$6p{>R$^yp}sk-o1bRs?JWX;Mn2cM=j?Zp8VOeJ@+;nLx$z{Dh7rP z_KXY+jzAAIu`n>Oa56A(2r)1Ss4y@ncrY+HOkiMWP@t`X3x!`B4$I7&JF|OrIfMGs z#@ucD*T^wm-}IEx!1(&v%r^{v?fd->g+6QNp}P}HVr&x@PEUV!^{TQ(@%nT7_idlk zYk%g<-_`C6>FZ?}Zl9^yTg8(8ZguW9?bYrv+I+<~!txo`@%hs?0O=4%Bi#yMLSXPa z_ORgb85?8c?Q7>1Y0axodbjt_-M{bueewVNWlwEI!T-}Pdku~MM^yaJ-<&J|rgn=A z!}{}QxL3OuJk$HnKfgLJO>X8)?({8B|KEFjUf=rfg-xeLY|V}5>+Y$qJ9@E*p=Qh3 zZ|7bKGHhu2&q(u3hZ3QEtJxWhjX&QyQ?se+i22{I$BXYTH$T6&`u^XGvp@fml|TP< z@B2-6zNbH1cU4)7g<($H^t5M}UJ2g+GF$z*`1Jg{_6%~r{_gqm;{CI=AcaQ-8FqgJ z7A90IRAj^XBVJgyTiyZYx8;u?ia%d=-_Gp%{QpJ&Z_D4a++I_2ye9wu>HF_q7r(qO zSm3Muzu^C?`|<|H#`QG`&+b;I>+RY5|82az>G_yn5C4@n>J2~+UGVr7qw(jHXKFT7 zvB+$*l-WP)cKWkld-LyqI9gNo@AS@1-|q&O9v0sxR + + + + + + + + diff --git a/taggui/dialogs/export_dialog.py b/taggui/dialogs/export_dialog.py index 0ef537e2..63223fa7 100644 --- a/taggui/dialogs/export_dialog.py +++ b/taggui/dialogs/export_dialog.py @@ -2,11 +2,11 @@ from collections import defaultdict import os import io -from math import floor +from math import ceil, floor from pathlib import Path import shutil -from PySide6.QtCore import QSize, Qt, Slot +from PySide6.QtCore import QRect, QSize, Qt, Slot from PySide6.QtGui import QColorSpace from PySide6.QtWidgets import (QWidget, QDialog, QFileDialog, QGridLayout, QLabel, QLineEdit, QPushButton, QTableWidget, @@ -20,6 +20,7 @@ from utils.settings_widgets import (SettingsBigCheckBox, SettingsLineEdit, SettingsSpinBox, SettingsComboBox) import utils.target_dimension as target_dimension +from utils.grid import Grid from widgets.image_list import ImageList try: @@ -27,45 +28,8 @@ except ModuleNotFoundError: pass -class ExportFilter(str, Enum): - NONE = 'All images' - FILTERED = 'Filtered images' - SELECTED = 'Selected images' - -Presets = { - 'manual': (0, 0, '1:1, 2:1, 3:2, 4:3, 16:9, 21:9'), - 'Direct feed through': (0, 1, '1:1, 2:1, 3:2, 4:3, 16:9, 21:9'), - 'SD1': (512, 64, '512:512, 640:320, 576:384, 512:384, 640:384, 768:320'), - 'SDXL, SD3, Flux': (1024, 64, '1024:1024, 1408:704, 1216:832, 1152:896, 1344:768, 1536:640') -} - -class ExportFormat(str, Enum): - JPG = '.jpg - JPEG' - JPGXL = '.jxl - JPEG XL' - PNG = '.png - PNG' - WEBP = '.webp - WEBP' - -ExportFormatDict = { - ExportFormat.JPG: 'jpeg', - ExportFormat.JPGXL: 'jxl', - ExportFormat.PNG: 'png', - ExportFormat.WEBP: 'webp' -} - -class IccProfileList(str, Enum): - SRgb = 'sRGB' - SRgbLinear = 'sRGB (linear gamma)' - AdobeRgb = 'AdobeRGB' - DisplayP3 = 'DisplayP3' - ProPhotoRgb = 'ProPhotoRGB' - Bt2020 = 'BT.2020' - Bt2100Pq = 'BT.2100(PQ)' - Bt2100Hlg = 'BT.2100 (HLG)' - -class BucketStrategy(str, Enum): - CROP = 'crop' - SCALE = 'scale' - CROP_SCALE = 'crop and scale' +from utils.enums import (ExportFilter, Presets, ExportFormat, ExportFormatDict, + IccProfileList, BucketStrategy) class ExportDialog(QDialog): def __init__(self, parent, image_list: ImageList): @@ -139,6 +103,32 @@ def __init__(self, parent, image_list: ImageList): grid_layout.addWidget(self.bucket_res_size_spin_box, grid_row, 1, Qt.AlignmentFlag.AlignLeft) + grid_row += 1 + grid_layout.addWidget(QLabel('Latent size (px)'), grid_row, 0, + Qt.AlignmentFlag.AlignRight) + latent_widget = QWidget() + latent_layout = QHBoxLayout() + latent_layout.setContentsMargins(0, 0, 0, 0) + self.latent_size_spin_box = SettingsSpinBox( + key='export_latent_size', + minimum=1, maximum=256) + self.latent_size_spin_box.setToolTip( + 'Size of one latent space pixel in image space pixels') + latent_layout.addWidget(self.latent_size_spin_box, + Qt.AlignmentFlag.AlignLeft) + latent_layout.addWidget(QLabel('Quantisize alpha channel'), + Qt.AlignmentFlag.AlignRight) + self.quantisize_alpha_check_box = SettingsBigCheckBox(key='export_quantisize_alpha') + self.quantisize_alpha_check_box.setToolTip( + 'Align the masks due to include and exclude marking with the\n' + 'latent space pixels.\n' + 'Only available when the output format supports an alpha channel.') + latent_layout.addWidget(self.quantisize_alpha_check_box, + Qt.AlignmentFlag.AlignLeft) + latent_widget.setLayout(latent_layout) + grid_layout.addWidget(latent_widget, grid_row, 1, + Qt.AlignmentFlag.AlignLeft) + grid_row += 1 grid_layout.addWidget(QLabel('Preferred sizes'), grid_row, 0, Qt.AlignmentFlag.AlignRight) @@ -283,6 +273,13 @@ def __init__(self, parent, image_list: ImageList): self.export_button.setEnabled(False) self.layout.addWidget(self.export_button) + image_list = self.get_image_list() + self.progress_bar = QProgressBar(self) + self.progress_bar.setMinimum(0) + self.progress_bar.setMaximum(len(image_list)) + self.progress_bar.hide() + self.layout.addWidget(self.progress_bar) + # update display self.apply_preset(preset_combo_box.currentText(), False) self.show_megapixels() @@ -300,15 +297,18 @@ def apply_preset(self, value: str, do_value_change: bool = True): if value == 'manual': self.resolution_spin_box.setEnabled(True) self.bucket_res_size_spin_box.setEnabled(True) + self.latent_size_spin_box.setEnabled(True) else: self.inhibit_statistics_update = True self.resolution_spin_box.setValue(preset[0]) if do_value_change else 0 self.resolution_spin_box.setEnabled(False) self.bucket_res_size_spin_box.setValue(preset[1]) if do_value_change else 0 self.bucket_res_size_spin_box.setEnabled(False) + self.latent_size_spin_box.setValue(preset[2]) if do_value_change else 0 + self.latent_size_spin_box.setEnabled(False) self.inhibit_statistics_update = inhibit_statistics_update_current self.show_statistics() - self.preferred_sizes_line_edit.setText(preset[2]) if do_value_change else 0 + self.preferred_sizes_line_edit.setText(preset[3]) if do_value_change else 0 @Slot() def show_megapixels(self): @@ -331,15 +331,19 @@ def format_change(self, export_format: ExportFormat, do_value_change: bool = Tru if export_format == ExportFormat.JPG: self.quality_spin_box.setValue(75) if do_value_change else 0 self.quality_spin_box.setEnabled(True) + self.quantisize_alpha_check_box.setEnabled(False) if export_format == ExportFormat.JPGXL: self.quality_spin_box.setValue(100) if do_value_change else 0 self.quality_spin_box.setEnabled(True) + self.quantisize_alpha_check_box.setEnabled(True) elif export_format == ExportFormat.PNG: self.quality_spin_box.setValue(100) if do_value_change else 0 self.quality_spin_box.setEnabled(False) + self.quantisize_alpha_check_box.setEnabled(True) elif export_format == ExportFormat.WEBP: self.quality_spin_box.setValue(80) if do_value_change else 0 self.quality_spin_box.setEnabled(True) + self.quantisize_alpha_check_box.setEnabled(True) @Slot() def quality_change(self, quality: str): @@ -456,59 +460,69 @@ def do_export(self): no_overwrite = True only_missing = True refresh_tags = False + path_missing = False + + try: + if os.path.exists(export_directory_path): + if os.path.isfile(export_directory_path): + QMessageBox.critical( + self, + 'Path error', + 'The export directory path points to a file and not to a directory' + ) + return + if os.listdir(export_directory_path): + msgBox = QMessageBox() + msgBox.setIcon(QMessageBox.Warning) + msgBox.setWindowTitle('Path warning') + msgBox.setText('The export directory path is not empty') + refresh_button = msgBox.addButton('Refresh', QMessageBox.ApplyRole) + refresh_button.setToolTip('Export only missing images, but update all captions') + overwrite_button = msgBox.addButton('Overwrite', QMessageBox.DestructiveRole) + refresh_button.setToolTip('Overwrite all existing files') + rename_button = msgBox.addButton('Rename', QMessageBox.YesRole) + refresh_button.setToolTip('Export with a new name') + only_missing_button = msgBox.addButton('Only missing', QMessageBox.AcceptRole) + refresh_button.setToolTip('Export only missing images') + msgBox.addButton(QMessageBox.Cancel) + msgBox.setDefaultButton(refresh_button) + button = msgBox.exec_() + if button == QMessageBox.Cancel: + return + if msgBox.clickedButton() == refresh_button: + no_overwrite = False + refresh_tags = True + if msgBox.clickedButton() == overwrite_button: + no_overwrite = False + only_missing = False + if msgBox.clickedButton() == rename_button: + only_missing = False + else: + path_missing = True + else: + path_missing = True - image_list = self.get_image_list() - self.progress_bar = QProgressBar(self) - self.progress_bar.setMinimum(0) - self.progress_bar.setMaximum(len(image_list)) - self.layout.addWidget(self.progress_bar) - - if os.path.exists(export_directory_path): - if os.path.isfile(export_directory_path): - QMessageBox.critical( + if path_missing: + button = QMessageBox.critical( self, 'Path error', - 'The export directory path points to a file and not to a directory' - ) - return - if os.listdir(export_directory_path): - msgBox = QMessageBox() - msgBox.setIcon(QMessageBox.Warning) - msgBox.setWindowTitle('Path warning') - msgBox.setText('The export directory path is not empty') - refresh_button = msgBox.addButton('Refresh', QMessageBox.ApplyRole) - refresh_button.setToolTip('Export only missing images, but update all captions') - overwrite_button = msgBox.addButton('Overwrite', QMessageBox.DestructiveRole) - refresh_button.setToolTip('Overwrite all existing files') - rename_button = msgBox.addButton('Rename', QMessageBox.YesRole) - refresh_button.setToolTip('Export with a new name') - only_missing_button = msgBox.addButton('Only missing', QMessageBox.AcceptRole) - refresh_button.setToolTip('Export only missing images') - msgBox.addButton(QMessageBox.Cancel) - msgBox.setDefaultButton(refresh_button) - button = msgBox.exec_() + 'The export directory path does not exist. Create it?', + QMessageBox.Ok, QMessageBox.Cancel) if button == QMessageBox.Cancel: return - if msgBox.clickedButton() == refresh_button: - no_overwrite = False - refresh_tags = True - if msgBox.clickedButton() == overwrite_button: - no_overwrite = False - only_missing = False - if msgBox.clickedButton() == rename_button: - only_missing = False - else: - QMessageBox.critical( - self, - 'Path error', - 'The export directory path does not exist' - ) + os.makedirs(export_directory_path) + except Exception as e: + QMessageBox.critical(self, 'Path error', f'Error: {e}') return + self.progress_bar.show() + tag_separator = settings.value('tag_separator', type=str) filter_hashtag = settings.value('export_filter_hashtag', type=bool) resolution = settings.value('export_resolution', type=int) bucket_res = settings.value('export_bucket_res_size', type=int) + latent_size = settings.value('export_latent_size', type=int) + quantisize_alpha = settings.value('export_quantisize_alpha', type=bool) export_format = settings.value('export_format', type=str) quality = settings.value('export_quality', type=int) color_space = settings.value('export_color_space', type=str) @@ -577,12 +591,12 @@ def do_export(self): if marking.type == ImageMarking.INCLUDE and export_can_alpha: if image_file.mode == 'RGB': image_file = image_file.convert("RGBA") - alpha = image_file.getchannel('A') # completely transparent - alpha.paste(0, (0, 0, current_width, current_height)) + alpha = Image.new('L', image_file.size, 0) else: alpha = image_file.getchannel('A') - alpha.paste(255, marking.rect.adjusted(0,0,1,1).getCoords()) + if not quantisize_alpha: + alpha.paste(255, marking.rect.adjusted(0,0,1,1).getCoords()) image_file.putalpha(alpha) # 2. pass: remove excludes @@ -590,34 +604,24 @@ def do_export(self): if marking.type == ImageMarking.EXCLUDE and export_can_alpha: if image_file.mode == 'RGB': image_file = image_file.convert("RGBA") - alpha = image_file.getchannel('A') # completely opaque - alpha.paste(255, (0, 0, current_width, current_height)) + alpha = Image.new('L', image_file.size, 255) else: alpha = image_file.getchannel('A') - alpha.paste(0, marking.rect.adjusted(0,0,1,1).getCoords()) + if not quantisize_alpha: + alpha.paste(0, marking.rect.adjusted(0,0,1,1).getCoords()) image_file.putalpha(alpha) - new_width, new_height = image_entry.target_dimension.toTuple() - crop_width, crop_height = image_entry.crop.size().toTuple() - if (bucket_strategy == BucketStrategy.CROP or - bucket_strategy == BucketStrategy.CROP_SCALE): - if crop_height * new_width / crop_width < new_height: # too wide - new_width = floor(crop_width * new_height / crop_height) - else: # too high - new_height = floor(crop_height * new_width / crop_width) - if bucket_strategy == BucketStrategy.CROP_SCALE: - new_width = floor((image_entry.target_dimension.width() + new_width)/2) - new_height = floor((image_entry.target_dimension.height() + new_height)/2) - if image_entry.crop == None: - cropped_image = image_file + grid = Grid(QRect(0, 0, *image_file.size)) else: - cropped_image = image_file.crop(image_entry.crop.adjusted(0,0,1,1).getCoords()) - - if cropped_image.size[0] != new_width or cropped_image.size[1] != new_height: + grid = Grid(image_entry.crop) + visible = grid.visible + cropped_image = image_file.crop(visible.adjusted(0,0,1,1).getCoords()) + if not grid.is_visible_equal_sceen_size(): # resize with the best method available - resized_image = cropped_image.resize((new_width, new_height), Image.LANCZOS) + #resized_image = cropped_image.resize((new_width, new_height), Image.LANCZOS) + resized_image = cropped_image.resize(grid.target.toTuple(), Image.LANCZOS) # followed by a slight sharpening as it should be done sharpend_image = resized_image.filter( ImageFilter.UnsharpMask(radius = 0.5, percent = 50, threshold = 0)) @@ -631,6 +635,25 @@ def do_export(self): cropped_image = sharpend_image.crop((crop_width, crop_height, crop_width + image_entry.target_dimension.width(), crop_height + image_entry.target_dimension.height())) + + if quantisize_alpha and export_can_alpha: + alpha = cropped_image.getchannel('A') + if image_entry.crop == None: + crop = QRect(0, 0, *image_entry.dimensions) + else: + crop = image_entry.crop + for marking in image_entry.markings: + if marking.type == ImageMarking.INCLUDE: + rect = QRect(grid.map(marking.rect.topLeft(), ceil), + grid.map(marking.rect.bottomRight(), floor)) + alpha.paste(255, rect.getCoords()) + for marking in image_entry.markings: + if marking.type == ImageMarking.EXCLUDE: + rect = QRect(grid.map(marking.rect.topLeft(), floor), + grid.map(marking.rect.bottomRight(), ceil)) + alpha.paste(0, rect.getCoords()) + cropped_image.putalpha(alpha) + lossless = quality > 99 if color_space == "feed through (don't touch)": diff --git a/taggui/models/image_list_model.py b/taggui/models/image_list_model.py index ab777af8..d1f191a2 100644 --- a/taggui/models/image_list_model.py +++ b/taggui/models/image_list_model.py @@ -194,10 +194,6 @@ def load_directory(self, directory_path: Path): crop = meta.get('crop') if crop and type(crop) is list and len(crop) == 4: image.crop = QRect(*crop) - target_dimension = meta.get('target_dimension') - if (target_dimension and type(target_dimension) is list - and len(target_dimension) == 2): - image.target_dimension = QSize(*target_dimension) markings = meta.get('markings') if markings and type(markings) is list: for marking in markings: @@ -242,10 +238,8 @@ def write_image_tags_to_disk(self, image: Image): def write_meta_to_disk(self, image: Image): does_exist = image.path.with_suffix('.json').exists() meta = {'version': 1} - if image.crop: + if image.crop != None: meta['crop'] = image.crop.getRect() - if image.target_dimension: - meta['target_dimension'] = image.target_dimension.toTuple() markings: list[dict[str, any]] = [] for marking in image.markings: markings.append({'label': marking.label, diff --git a/taggui/utils/enums.py b/taggui/utils/enums.py index 21cb60c8..975a47e6 100644 --- a/taggui/utils/enums.py +++ b/taggui/utils/enums.py @@ -24,3 +24,43 @@ class CaptionPosition(str, Enum): class CaptionDevice(str, Enum): GPU = 'GPU if available' CPU = 'CPU' + +class ExportFilter(str, Enum): + NONE = 'All images' + FILTERED = 'Filtered images' + SELECTED = 'Selected images' + +Presets = { + 'manual': (0, 0, 1, '1:1, 2:1, 3:2, 4:3, 16:9, 21:9'), + 'Direct feed through': (0, 1, 1, '1:1, 2:1, 3:2, 4:3, 16:9, 21:9'), + 'SD1': (512, 64, 8, '512:512, 640:320, 576:384, 512:384, 640:384, 768:320'), + 'SDXL, SD3, Flux': (1024, 64, 8, '1024:1024, 1408:704, 1216:832, 1152:896, 1344:768, 1536:640') +} + +class ExportFormat(str, Enum): + JPG = '.jpg - JPEG' + JPGXL = '.jxl - JPEG XL' + PNG = '.png - PNG' + WEBP = '.webp - WEBP' + +ExportFormatDict = { + ExportFormat.JPG: 'jpeg', + ExportFormat.JPGXL: 'jxl', + ExportFormat.PNG: 'png', + ExportFormat.WEBP: 'webp' +} + +class IccProfileList(str, Enum): + SRgb = 'sRGB' + SRgbLinear = 'sRGB (linear gamma)' + AdobeRgb = 'AdobeRGB' + DisplayP3 = 'DisplayP3' + ProPhotoRgb = 'ProPhotoRGB' + Bt2020 = 'BT.2020' + Bt2100Pq = 'BT.2100(PQ)' + Bt2100Hlg = 'BT.2100 (HLG)' + +class BucketStrategy(str, Enum): + CROP = 'crop' + SCALE = 'scale' + CROP_SCALE = 'crop and scale' diff --git a/taggui/utils/grid.py b/taggui/utils/grid.py new file mode 100644 index 00000000..03cc342a --- /dev/null +++ b/taggui/utils/grid.py @@ -0,0 +1,90 @@ +from math import ceil, floor +from PySide6.QtCore import (QPoint, QPointF, QRect, QRectF, QSize, QSizeF) +from utils.enums import BucketStrategy +from utils.settings import settings +import utils.target_dimension as target_dimension + +class Grid: + """Span a grid inside a the sceen. + + The screen is adjusted according to bucket strategy and scaled to the + target dimension and then the grid respects the latent size. + """ + + def __init__(self, screen: QRect): + # the full image or the user cropped part of it + self.screen: QRect + # the visible part of the screen, i.e. the bucket cropped part of it + self.visible: QRect + # the size of the exported image + self.target: QSize + self.scale_x: float + self.scale_y: float + + self.update(screen) + + def update(self, screen: QRect | None = None): + assert screen == None or isinstance(screen, QRect) + bucket_res = settings.value('export_bucket_res_size', type=int) + bucket_strategy = settings.value('export_bucket_strategy', type=str) + if screen != None: + self.screen = screen + + if self.screen.width() == 0 or self.screen.height() == 0: + self.visible = self.screen + self.target = QSize(1, 1) + self.scale_x = 1 + self.scale_y = 1 + return + + vis_size = self.screen.size() + self.target = target_dimension.get(vis_size) + + if (bucket_strategy == BucketStrategy.CROP or + bucket_strategy == BucketStrategy.CROP_SCALE): + if (self.screen.height() * self.target.width() + < self.target.height() * self.screen.width()): # too wide + vis_size.setWidth(floor(self.screen.height() * self.target.width() / self.target.height())) + else: + vis_size.setHeight(floor(self.screen.width() * self.target.height() / self.target.width())) + if bucket_strategy == BucketStrategy.CROP_SCALE: + vis_size.setWidth(floor((self.screen.width() + vis_size.width())/2)) + vis_size.setHeight(floor((self.screen.height() + vis_size.height())/2)) + + delta = self.screen.size() - vis_size + self.visible = self.screen.adjusted(floor(delta.width()/2), + floor(delta.height()/2), + -ceil(delta.width()/2), + -ceil(delta.height()/2)) + + self.scale_x = self.target.width() / self.visible.width() + self.scale_y = self.target.height() / self.visible.height() + + def is_visible_equal_sceen_size(self) -> bool: + return self.screen.size() == self.visible + + def map_raw(self, point: QPoint) -> QPointF: + """Translate point into screen coordinates.""" + assert isinstance(point, QPoint) + return QPointF((point.x()-self.visible.x())*self.scale_x, + (point.y()-self.visible.y())*self.scale_y) + + + def map(self, point: QPoint, method = round) -> QPoint: + """Align the point to the closest position on the grid aligned at + `base_point` and with a step width of `grid`. + """ + assert isinstance(point, QPoint) + latent_size = settings.value('export_latent_size', type=int) + raw = self.map_raw(point) + return QPoint(method(raw.x()/latent_size)*latent_size, + method(raw.y()/latent_size)*latent_size) + + def snap(self, point: QPoint, method = round) -> QPointF: + """Align the point to the closes position on the grid but in + screen coordinates. + """ + assert isinstance(point, QPoint) + mapped = self.map(point, method) + return QPointF(mapped.x()/self.scale_x + self.visible.x(), + mapped.y()/self.scale_y + self.visible.y()) diff --git a/taggui/utils/settings.py b/taggui/utils/settings.py index 9d5ac600..91103946 100644 --- a/taggui/utils/settings.py +++ b/taggui/utils/settings.py @@ -14,6 +14,8 @@ 'export_preset': 'SDXL, SD3, Flux', 'export_resolution': 1024, 'export_bucket_res_size': 64, + 'export_latent_size': 8, + 'export_quantisize_alpha': True, 'export_preferred_sizes' : '1024:1024, 1408:704, 1216:832, 1152:896, 1344:768, 1536:640', 'export_upscaling': False, 'export_bucket_strategy': 'crop', diff --git a/taggui/widgets/image_viewer.py b/taggui/widgets/image_viewer.py index fcbf0442..6c31f9f6 100644 --- a/taggui/widgets/image_viewer.py +++ b/taggui/widgets/image_viewer.py @@ -1,5 +1,5 @@ from enum import Enum -from math import floor +from math import ceil, floor from pathlib import Path from PySide6.QtCore import (QEvent, QModelIndex, QObject, QPersistentModelIndex, QPoint, QPointF, QRect, QRectF, QSize, QSizeF, Qt, @@ -14,13 +14,11 @@ from models.proxy_image_list_model import ProxyImageListModel from utils.image import Image, ImageMarking, Marking import utils.target_dimension as target_dimension +from utils.grid import Grid from dialogs.export_dialog import BucketStrategy -# Alignment base for a grid -base_point: QPoint = QPoint(0, 0) - -# stepsize of the grid -grid: int = 8 +# Grid for alignment to latent space +grid = Grid(QRect(0, 0, 1, 1)) marking_colors = { ImageMarking.CROP: Qt.blue, @@ -115,11 +113,9 @@ def change_rect_to_match_size(rect: QRect, rect_pos: RectPosition, size: QSize) rect_new.moveRight(rect.right()) return rect_new -def map_to_grid(point: QPoint) -> QPoint: - """Align the point to the closest position on the grid aligned at - `base_point` and with a step width of `grid`. - """ - return (QPointF(point - base_point) / grid).toPoint() * grid + base_point +def calculate_grid(content: QRect): + global grid + grid = Grid(content) class RectItemSignal(QObject): change = Signal(QGraphicsRectItem, name='rectChanged') @@ -134,13 +130,12 @@ class MarkingItem(QGraphicsRectItem): zoom_factor = 1.0 # The size of the image this rect belongs to image_size = QRect(0, 0, 1, 1) - # the last (quantisized position of the mouse - #last_pos = None # Static link to the single ImageGraphicsView in this application image_view = None + show_marking_latent = True def __init__(self, rect: QRect, rect_type: ImageMarking, - target_size: QSize | None = None, parent = None): + parent = None): super().__init__(rect.toRectF(), parent) self.setFlag(QGraphicsItem.ItemIsSelectable, True) self.setFlag(QGraphicsItem.ItemSendsScenePositionChanges, True) @@ -149,10 +144,6 @@ def __init__(self, rect: QRect, rect_type: ImageMarking, self.rect_type = rect_type self.label: MarkingLabel | None = None self.color = marking_colors[rect_type] - if rect_type == ImageMarking.CROP and target_size == None: - self.size_changed() # this method sets self.target_size - else: - self.target_size = target_size self.handle_selected = None self.mouse_press_pos = None self.mouse_press_rect = None @@ -206,20 +197,6 @@ def mousePressEvent(self, event): self.mouse_press_scene_pos = event.scenePos() self.mouse_press_rect = self.rect() self.signal.move.emit(self.rect(), self.handle_selected) - if self.rect_type == ImageMarking.CROP: - global base_point - if (self.handle_selected == RectPosition.TL or - self.handle_selected == RectPosition.TOP): - base_point = self.rect().bottomRight().toPoint() - elif (self.handle_selected == RectPosition.TR or - self.handle_selected == RectPosition.RIGHT): - base_point = self.rect().bottomLeft().toPoint() - elif (self.handle_selected == RectPosition.BR or - self.handle_selected == RectPosition.BOTTOM): - base_point = self.rect().topLeft().toPoint() - elif (self.handle_selected == RectPosition.BL or - self.handle_selected == RectPosition.LEFT): - base_point = self.rect().topRight().toPoint() else: event.ignore() @@ -255,7 +232,7 @@ def mouseMoveEvent(self, event): self.handle_selected, target_size) else: - pos_quantizised = map_to_grid(event.pos().toPoint()) + pos_quantizised = grid.snap(event.pos().toPoint()).toPoint() rect = change_rect(self.rect().toRect(), self.handle_selected, pos_quantizised) @@ -287,17 +264,24 @@ def mouseReleaseEvent(self, event): super().mouseReleaseEvent(event) def paint(self, painter, option, widget=None): + bucket_strategy = settings.value('export_bucket_strategy', type=str) if self.rect_type == ImageMarking.CROP: painter.setPen(Qt.NoPen) painter.setBrush(QColor(255, 0, 0, 127)) path = QPainterPath() path.addRect(self.rect()) - if self.target_size: - to_crop = self.rect().size() - self.target_size - path.addRect(QRectF(QPointF(self.rect().x()+floor(to_crop.width()/2), - self.rect().y()+floor(to_crop.height()/2)), - self.target_size)) + path.addRect(grid.visible) painter.drawPath(path) + elif self.rect_type == ImageMarking.INCLUDE and self.show_marking_latent: + painter.setPen(Qt.NoPen) + painter.setBrush(QColor(0, 255, 0, 127)) + painter.drawRect(QRectF(grid.snap(self.rect().toRect().topLeft(), ceil), + grid.snap(self.rect().toRect().bottomRight(), floor))) + elif self.rect_type == ImageMarking.EXCLUDE and self.show_marking_latent: + painter.setPen(Qt.NoPen) + painter.setBrush(QColor(255, 0, 0, 127)) + painter.drawRect(QRectF(grid.snap(self.rect().toRect().topLeft(), floor), + grid.snap(self.rect().toRect().bottomRight(), ceil))) pen_half_width = self.pen_half_width / self.zoom_factor pen = QPen(self.color, 2*pen_half_width, Qt.SolidLine, Qt.RoundCap, @@ -325,23 +309,18 @@ def shape(self): def boundingRect(self): adjust = (self.pen_half_width + max(self.pen_half_width, self.handle_half_size))/self.zoom_factor - return self.rect().adjusted(-adjust, -adjust, adjust, adjust) + bbox = self.rect().adjusted(-adjust, -adjust, adjust, adjust) + if self.rect_type == ImageMarking.EXCLUDE: + bbox = bbox.united(QRectF(grid.snap(self.rect().toRect().topLeft(), floor), + grid.snap(self.rect().toRect().bottomRight(), ceil))) + return bbox def size_changed(self): if self.rect_type == ImageMarking.CROP: - bucket_strategy = settings.value('export_bucket_strategy', type=str) - current = self.rect().size().expandedTo(QSize(1, 1)) - if bucket_strategy == BucketStrategy.SCALE: - self.target_size = current - else: # CROP or CROP_SCALE - target = target_dimension.get(current) - if current.height() * target.width() / current.width() < target.height(): # too wide - scale = current.height() / target.height() - else: # too high - scale = current.width() / target.width() - self.target_size = QSize(round(target.width()*scale), round(target.height()*scale)) - if bucket_strategy == BucketStrategy.CROP_SCALE: - self.target_size = (self.target_size + current.toSize())/2 + old_grid = grid + calculate_grid(self.rect().toRect()) + if old_grid != grid: + self.image_view.image_viewer.recalculate_markings(self) self.adjust_layout() def adjust_layout(self): @@ -542,12 +521,9 @@ def showContextMenu(self, pos): def clear_scene(self): """Use this and not scene.clear() due to resource management.""" - global grid, base_point self.insertion_mode = False self.horizontal_line = None self.vertical_line = None - grid = 8 - base_point = QPoint(0, 0) self.scene().clear() def set_insertion_mode(self, mode): @@ -582,7 +558,6 @@ def update_lines_pos(self): self.vertical_line.setLine(self.last_pos.x(), view_rect.top(), self.last_pos.x(), view_rect.bottom()) - def mousePressEvent(self, event): if self.insertion_mode and event.button() == Qt.MouseButton.LeftButton: rect_type = self.image_viewer.marking_to_add @@ -604,7 +579,7 @@ def mouseMoveEvent(self, event): last_pos_raw = self.mapToScene(event.position().toPoint()) if ((event.modifiers() & Qt.KeyboardModifier.ShiftModifier) == Qt.KeyboardModifier.ShiftModifier): - self.last_pos = map_to_grid(last_pos_raw.toPoint()) + self.last_pos = grid.snap(last_pos_raw.toPoint()).toPoint() else: self.last_pos = last_pos_raw.toPoint() pos = last_pos_raw @@ -644,9 +619,11 @@ def __init__(self, proxy_image_list_model: ProxyImageListModel): self.is_zoom_to_fit = True self.show_marking_state = True self.show_label_state = True + self.show_marking_latent_state = True self.marking_to_add = ImageMarking.NONE self.scene = QGraphicsScene() self.view = ImageGraphicsView(self.scene, self) + self.crop_marking: ImageMarking | None = None settings.change.connect(self.setting_change) layout = QVBoxLayout() @@ -681,22 +658,30 @@ def load_image(self, proxy_image_index: QModelIndex): self.marking_to_add = ImageMarking.NONE self.marking.emit(ImageMarking.NONE) - self.accept_crop_addition.emit(not image.crop) - if image.crop: - if not image.target_dimension: - image.target_dimension = target_dimension.get(image.crop.size()) - self.add_rectangle(image.crop, ImageMarking.CROP, - size=image.target_dimension) + self.accept_crop_addition.emit(image.crop == None) + if image.crop != None: + self.add_rectangle(image.crop, ImageMarking.CROP) + else: + calculate_grid(MarkingItem.image_size) for marking in image.markings: self.add_rectangle(marking.rect, marking.type, name=marking.label) @Slot() def setting_change(self, key, value): if key in ['export_resolution', 'export_bucket_res_size', - 'export_upscaling', 'export_bucket_strategy']: - for marking in self.marking_items: + 'export_latent_size', 'export_upscaling', + 'export_bucket_strategy']: + self.recalculate_markings() + + def recalculate_markings(self, ignore: MarkingItem | None = None): + if self.crop_marking: + calculate_grid(self.crop_marking.rect().toRect()) + else: + calculate_grid(MarkingItem.image_size) + for marking in self.marking_items: + if marking != ignore: marking.size_changed() - self.scene.invalidate() + self.scene.invalidate() @Slot() def zoom_in(self, center_pos: QPoint = None): @@ -745,7 +730,6 @@ def zoom_emit(self): def add_marking(self, marking: ImageMarking): self.marking_to_add = marking self.view.set_insertion_mode(marking != ImageMarking.NONE) - grid = 1 if marking == ImageMarking.CROP else 8 @Slot() def change_marking(self, items: list[MarkingItem] | None = None, @@ -780,6 +764,13 @@ def show_label(self, checked: bool): marking.label.setVisible(checked) marking.label.parentItem().setVisible(checked) + @Slot(bool) + def show_marking_latent(self, checked: bool): + MarkingItem.show_marking_latent = checked + for marking in self.marking_items: + if marking.rect_type in [ImageMarking.INCLUDE, ImageMarking.EXCLUDE]: + marking.update() + def wheelEvent(self, event): old_pos = self.view.mapToScene(event.position().toPoint()) @@ -799,6 +790,8 @@ def add_rectangle(self, rect: QRect, rect_type: ImageMarking, marking_item.setVisible(self.show_marking_state) if rect_type == ImageMarking.CROP: marking_item.signal.move.connect(self.hud_item.setValues) + self.crop_marking = marking_item + marking_item.size_changed() # call after self.crop_marking was set! elif name == '' and rect_type != ImageMarking.NONE: image: Image = self.proxy_image_index.data(Qt.ItemDataRole.UserRole) name = {ImageMarking.HINT: 'hint', @@ -821,10 +814,11 @@ def add_rectangle(self, rect: QRect, rect_type: ImageMarking, self.marking.emit(ImageMarking.NONE) if rect_type == ImageMarking.CROP: self.accept_crop_addition.emit(False) - self.marking_changed(marking_item) @Slot() def label_changed(self, do_emit = True): + """Slot to call when a marking label was changed to sync the information + in the image.""" image: Image = self.proxy_image_index.data(Qt.ItemDataRole.UserRole) image.markings.clear() for marking in self.marking_items: @@ -839,7 +833,8 @@ def label_changed(self, do_emit = True): @Slot(QGraphicsRectItem) def marking_changed(self, marking: QGraphicsRectItem): - global grid, base_point + """Slot to call when a marking was changed to sync the information + in the image.""" assert self.proxy_image_index != None assert self.proxy_image_index.isValid() image: Image = self.proxy_image_index.data(Qt.ItemDataRole.UserRole) @@ -847,11 +842,7 @@ def marking_changed(self, marking: QGraphicsRectItem): if marking.rect_type == ImageMarking.CROP: image.thumbnail = None image.crop = marking.rect().toRect() # ensure int! - image.target_dimension = marking.target_size - scale = min(image.crop.width()/image.target_dimension.width(), - image.crop.height()/image.target_dimension.height()) - base_point = QPoint(image.crop.x()+floor((image.crop.width()-scale*image.target_dimension.width())/2), - image.crop.y()+floor((image.crop.height()-scale*image.target_dimension.height())/2)) + image.target_dimension = grid.target else: image.markings = [Marking(marking.data(0), marking.rect_type, @@ -867,17 +858,19 @@ def get_selected_type(self) -> ImageMarking: @Slot() def delete_markings(self, items: list[MarkingItem] | None = None): + """Slot to delete the list of items or when items = None all currently + selected marking items.""" image: Image = self.proxy_image_index.data(Qt.ItemDataRole.UserRole) if items == None: items = self.scene.selectedItems() for item in items: if item.rect_type == ImageMarking.CROP: - global base_point + self.crop_marking = None image.thumbnail = None image.crop = None image.target_dimension = None - base_point = QPoint(0, 0) self.accept_crop_addition.emit(True) + calculate_grid(MarkingItem.image_size) else: self.marking_items.remove(item) self.label_changed(False) diff --git a/taggui/widgets/main_window.py b/taggui/widgets/main_window.py index e983996b..0c29cac6 100644 --- a/taggui/widgets/main_window.py +++ b/taggui/widgets/main_window.py @@ -35,6 +35,7 @@ TOGGLE_MARKING_ICON_PATH = Path('images/toggle_marking.png') SHOW_MARKINGS_ICON_PATH = Path('images/show_marking.png') SHOW_LABELS_ICON_PATH = Path('images/show_label.png') +SHOW_MARKING_LATENT_ICON_PATH = Path('images/show_marking_latent.png') GITHUB_REPOSITORY_URL = 'https://github.com/jhc13/taggui' TOKENIZER_DIRECTORY_PATH = Path('clip-vit-base-patch32') @@ -130,6 +131,12 @@ def __init__(self, app: QApplication): self.add_show_labels_action.setCheckable(True) self.add_show_labels_action.setChecked(True) self.toolbar.addAction(self.add_show_labels_action) + self.add_show_marking_latent_action = QAction( + QIcon(QPixmap(get_resource_path(SHOW_MARKING_LATENT_ICON_PATH))), + 'Show marking in latent space', self) + self.add_show_marking_latent_action.setCheckable(True) + self.add_show_marking_latent_action.setChecked(True) + self.toolbar.addAction(self.add_show_marking_latent_action) self.image_list = ImageList(self.proxy_image_list_model, tag_separator, image_list_image_width) @@ -558,10 +565,14 @@ def connect_toolbar_signals(self): self.delete_marking_action.triggered.connect(lambda: self.image_viewer.delete_markings()) self.add_show_marking_action.toggled.connect(self.image_viewer.show_marking) self.add_show_marking_action.toggled.connect(self.add_action_group.setEnabled) - self.add_show_marking_action.toggled.connect(self.add_toggle_marking_action.setEnabled) + self.add_show_marking_action.toggled.connect(lambda toggled: + self.add_toggle_marking_action.setEnabled(toggled and + self.image_viewer.get_selected_type() != ImageMarking.NONE)) self.add_show_marking_action.toggled.connect(self.add_show_labels_action.setEnabled) - self.add_toggle_marking_action.triggered.connect(self.image_viewer.change_marking) + self.add_show_marking_action.toggled.connect(self.add_show_marking_latent_action.setEnabled) + self.add_toggle_marking_action.triggered.connect(lambda: self.image_viewer.change_marking()) self.add_show_labels_action.toggled.connect(self.image_viewer.show_label) + self.add_show_marking_latent_action.toggled.connect(self.image_viewer.show_marking_latent) def connect_image_list_signals(self): self.image_list.filter_line_edit.textChanged.connect( From c7773ac982df2c173dc6853fa35e8b39440a410b Mon Sep 17 00:00:00 2001 From: StableLlama Date: Sun, 9 Mar 2025 23:21:37 +0100 Subject: [PATCH 35/82] Add masked content modification --- README.md | 20 +++++++++ taggui/dialogs/export_dialog.py | 72 +++++++++++++++++++++++++-------- taggui/utils/enums.py | 9 +++++ taggui/utils/settings.py | 1 + 4 files changed, 85 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index 925a730c..314da3b6 100644 --- a/README.md +++ b/README.md @@ -285,6 +285,26 @@ number. `Bucket resolution size`: The bucket size the training tool is using. +`Latent size`: +The size of one latent space pixel in image pixels. + +`Quantisize alpha channel`: +When exporting with include or exclude markings in an image fortmat that +supports alpha masks (all, but not the classic JPEG) you can make sure that +the masks are aligned to the latent pixels that the trainer is using for +masked training. + +`Masked content`: +When exporting with include or exclude markings in an image fortmat that +supports alpha masks (all, but not the classic JPEG) you can change the +content that is invisible due to the mask. +It is known that some masked content can slightly bleed through during the +masked training, so it can be beneficial when this (supposed to be invisible) +content is replaced. In simple cases (e.g. masking the face for cloth LoRA +training) the default "blur + noise" is a good choice. In hard cases to really +hide the original data "grey + noise" can be a good choice. "original" doesn't +modify the data behind the mask. + `Preferres sizes`: A comma separated list of target sizes that should be preferred for the exported images. diff --git a/taggui/dialogs/export_dialog.py b/taggui/dialogs/export_dialog.py index 63223fa7..b0bce043 100644 --- a/taggui/dialogs/export_dialog.py +++ b/taggui/dialogs/export_dialog.py @@ -5,6 +5,7 @@ from math import ceil, floor from pathlib import Path import shutil +import numpy as np from PySide6.QtCore import QRect, QSize, Qt, Slot from PySide6.QtGui import QColorSpace @@ -28,8 +29,8 @@ except ModuleNotFoundError: pass -from utils.enums import (ExportFilter, Presets, ExportFormat, ExportFormatDict, - IccProfileList, BucketStrategy) +from utils.enums import (ExportFilter, Presets, MaskedContent, ExportFormat, + ExportFormatDict, IccProfileList, BucketStrategy) class ExportDialog(QDialog): def __init__(self, parent, image_list: ImageList): @@ -125,6 +126,12 @@ def __init__(self, parent, image_list: ImageList): 'Only available when the output format supports an alpha channel.') latent_layout.addWidget(self.quantisize_alpha_check_box, Qt.AlignmentFlag.AlignLeft) + latent_layout.addWidget(QLabel('Masked content'), + Qt.AlignmentFlag.AlignRight) + self.masked_content_combo_box = SettingsComboBox(key='export_masked_content') + self.masked_content_combo_box.addItems(list(MaskedContent)) + latent_layout.addWidget(self.masked_content_combo_box, + Qt.AlignmentFlag.AlignLeft) latent_widget.setLayout(latent_layout) grid_layout.addWidget(latent_widget, grid_row, 1, Qt.AlignmentFlag.AlignLeft) @@ -332,18 +339,22 @@ def format_change(self, export_format: ExportFormat, do_value_change: bool = Tru self.quality_spin_box.setValue(75) if do_value_change else 0 self.quality_spin_box.setEnabled(True) self.quantisize_alpha_check_box.setEnabled(False) + self.masked_content_combo_box.setEnabled(False) if export_format == ExportFormat.JPGXL: self.quality_spin_box.setValue(100) if do_value_change else 0 self.quality_spin_box.setEnabled(True) self.quantisize_alpha_check_box.setEnabled(True) + self.masked_content_combo_box.setEnabled(True) elif export_format == ExportFormat.PNG: self.quality_spin_box.setValue(100) if do_value_change else 0 self.quality_spin_box.setEnabled(False) self.quantisize_alpha_check_box.setEnabled(True) + self.masked_content_combo_box.setEnabled(True) elif export_format == ExportFormat.WEBP: self.quality_spin_box.setValue(80) if do_value_change else 0 self.quality_spin_box.setEnabled(True) self.quantisize_alpha_check_box.setEnabled(True) + self.masked_content_combo_box.setEnabled(True) @Slot() def quality_change(self, quality: str): @@ -523,6 +534,7 @@ def do_export(self): bucket_res = settings.value('export_bucket_res_size', type=int) latent_size = settings.value('export_latent_size', type=int) quantisize_alpha = settings.value('export_quantisize_alpha', type=bool) + masked_content = settings.value('export_masked_content', type=str) export_format = settings.value('export_format', type=str) quality = settings.value('export_quality', type=int) color_space = settings.value('export_color_space', type=str) @@ -636,22 +648,48 @@ def do_export(self): crop_width + image_entry.target_dimension.width(), crop_height + image_entry.target_dimension.height())) - if quantisize_alpha and export_can_alpha: + if export_can_alpha: alpha = cropped_image.getchannel('A') - if image_entry.crop == None: - crop = QRect(0, 0, *image_entry.dimensions) - else: - crop = image_entry.crop - for marking in image_entry.markings: - if marking.type == ImageMarking.INCLUDE: - rect = QRect(grid.map(marking.rect.topLeft(), ceil), - grid.map(marking.rect.bottomRight(), floor)) - alpha.paste(255, rect.getCoords()) - for marking in image_entry.markings: - if marking.type == ImageMarking.EXCLUDE: - rect = QRect(grid.map(marking.rect.topLeft(), floor), - grid.map(marking.rect.bottomRight(), ceil)) - alpha.paste(0, rect.getCoords()) + if quantisize_alpha: + if image_entry.crop == None: + crop = QRect(0, 0, *image_entry.dimensions) + else: + crop = image_entry.crop + for marking in image_entry.markings: + if marking.type == ImageMarking.INCLUDE: + rect = QRect(grid.map(marking.rect.topLeft(), ceil), + grid.map(marking.rect.bottomRight(), floor)) + alpha.paste(255, rect.getCoords()) + for marking in image_entry.markings: + if marking.type == ImageMarking.EXCLUDE: + rect = QRect(grid.map(marking.rect.topLeft(), floor), + grid.map(marking.rect.bottomRight(), ceil)) + alpha.paste(0, rect.getCoords()) + + replacement = None + if masked_content in [MaskedContent.BLUR, MaskedContent.BLUR_NOISE]: + replacement = cropped_image.filter(ImageFilter.GaussianBlur(10)) + elif masked_content in [MaskedContent.GREY, MaskedContent.GREY_NOISE]: + # 126 is an 18% grey, i.e. the neutral grey, for sRGB + # as it's masked there's no need to go into detail about + # different color spaces. + replacement = Image.new('RGB', cropped_image.size, (126, 126, 126)) + elif masked_content == MaskedContent.BLACK: + replacement = Image.new('RGB', cropped_image.size, (0, 0, 0)) + elif masked_content == MaskedContent.WHITE: + replacement = Image.new('RGB', cropped_image.size, (255, 255, 255)) + + if masked_content in [MaskedContent.BLUR_NOISE, MaskedContent.GREY_NOISE]: + np_image = np.array(replacement) + # Add random noise with a minimal blur + noise = np.random.normal(0, 40, np_image.shape).astype(np.uint8) + noisy_image = np_image + noise + noisy_image = np.clip(noisy_image, 0, 255) + replacement = Image.fromarray(noisy_image).filter(ImageFilter.GaussianBlur(1)) + + if replacement: + cropped_image = Image.composite(cropped_image, replacement, alpha) + cropped_image.putalpha(alpha) lossless = quality > 99 diff --git a/taggui/utils/enums.py b/taggui/utils/enums.py index 975a47e6..4a1b49e4 100644 --- a/taggui/utils/enums.py +++ b/taggui/utils/enums.py @@ -37,6 +37,15 @@ class ExportFilter(str, Enum): 'SDXL, SD3, Flux': (1024, 64, 8, '1024:1024, 1408:704, 1216:832, 1152:896, 1344:768, 1536:640') } +class MaskedContent(str, Enum): + ORIGINAL = 'original' + BLUR = 'blur' + BLUR_NOISE = 'blur + noise' + GREY = 'grey' + GREY_NOISE = 'grey + noise' + BLACK = 'black' + WHITE = 'white' + class ExportFormat(str, Enum): JPG = '.jpg - JPEG' JPGXL = '.jxl - JPEG XL' diff --git a/taggui/utils/settings.py b/taggui/utils/settings.py index 91103946..b2a07a2d 100644 --- a/taggui/utils/settings.py +++ b/taggui/utils/settings.py @@ -16,6 +16,7 @@ 'export_bucket_res_size': 64, 'export_latent_size': 8, 'export_quantisize_alpha': True, + 'export_masked_content': 'blur + noise', 'export_preferred_sizes' : '1024:1024, 1408:704, 1216:832, 1152:896, 1344:768, 1536:640', 'export_upscaling': False, 'export_bucket_strategy': 'crop', From 0166329a5e59d85f478c17183db8827a16629956 Mon Sep 17 00:00:00 2001 From: StableLlama Date: Mon, 10 Mar 2025 20:13:34 +0100 Subject: [PATCH 36/82] Add tooltip to image list Also optimize the export dialog a little bit --- taggui/dialogs/export_dialog.py | 33 +++++++++++++++++++------------ taggui/models/image_list_model.py | 15 +++++++++++++- 2 files changed, 34 insertions(+), 14 deletions(-) diff --git a/taggui/dialogs/export_dialog.py b/taggui/dialogs/export_dialog.py index b0bce043..0cf134a7 100644 --- a/taggui/dialogs/export_dialog.py +++ b/taggui/dialogs/export_dialog.py @@ -10,10 +10,10 @@ from PySide6.QtCore import QRect, QSize, Qt, Slot from PySide6.QtGui import QColorSpace from PySide6.QtWidgets import (QWidget, QDialog, QFileDialog, QGridLayout, - QLabel, QLineEdit, QPushButton, QTableWidget, - QTableWidgetItem, QProgressBar, QMessageBox, - QVBoxLayout, QHBoxLayout, QSizePolicy, - QAbstractItemView) + QHeaderView, QLabel, QLineEdit, QPushButton, + QTableWidget, QTableWidgetItem, QProgressBar, + QMessageBox, QVBoxLayout, QHBoxLayout, + QSizePolicy, QAbstractItemView) from PIL import Image, ImageFilter, ImageCms from utils.settings import DEFAULT_SETTINGS, settings @@ -70,6 +70,9 @@ def __init__(self, parent, image_list: ImageList): grid_row += 1 grid_layout.addWidget(QLabel('Resolution (px)'), grid_row, 0, Qt.AlignmentFlag.AlignRight) + resolution_widget = QWidget() + resolution_layout = QHBoxLayout() + resolution_layout.setContentsMargins(0, 0, 0, 0) self.resolution_spin_box = SettingsSpinBox( key='export_resolution', minimum=0, maximum=8192) @@ -80,14 +83,16 @@ def __init__(self, parent, image_list: ImageList): '1024: SDXL, SD3, Flux') self.resolution_spin_box.valueChanged.connect(self.show_megapixels) self.resolution_spin_box.valueChanged.connect(self.show_statistics) - grid_layout.addWidget(self.resolution_spin_box, grid_row, 1, - Qt.AlignmentFlag.AlignLeft) + resolution_layout.addWidget(self.resolution_spin_box, + Qt.AlignmentFlag.AlignLeft) - grid_row += 1 - grid_layout.addWidget(QLabel('Image size (megapixel)'), grid_row, 0, - Qt.AlignmentFlag.AlignRight) + resolution_layout.addWidget(QLabel('Image size (megapixel)'), + Qt.AlignmentFlag.AlignRight) self.megapixels = QLabel('-') - grid_layout.addWidget(self.megapixels, grid_row, 1, + resolution_layout.addWidget(self.megapixels, + Qt.AlignmentFlag.AlignLeft) + resolution_widget.setLayout(resolution_layout) + grid_layout.addWidget(resolution_widget, grid_row, 1, Qt.AlignmentFlag.AlignLeft) grid_row += 1 @@ -141,7 +146,7 @@ def __init__(self, parent, image_list: ImageList): Qt.AlignmentFlag.AlignRight) self.preferred_sizes_line_edit = SettingsLineEdit( key='export_preferred_sizes') - self.preferred_sizes_line_edit.setMinimumWidth(500) + self.preferred_sizes_line_edit.setMinimumWidth(600) self.preferred_sizes_line_edit.setToolTip( 'Comma separated list of preferred sizes and aspect ratios.\n' "The inverse aspect ratio is automatically derived and doesn't need to be included.") @@ -230,7 +235,7 @@ def __init__(self, parent, image_list: ImageList): Qt.AlignmentFlag.AlignRight) self.export_directory_line_edit = SettingsLineEdit( key='export_directory_path') - self.export_directory_line_edit.setMinimumWidth(500) + self.export_directory_line_edit.setMinimumWidth(600) self.export_directory_line_edit.setClearButtonEnabled(True) grid_layout.addWidget(self.export_directory_line_edit, grid_row, 1, Qt.AlignmentFlag.AlignLeft) @@ -258,7 +263,9 @@ def __init__(self, parent, image_list: ImageList): self.statistics_table = QTableWidget(0, 5, self) self.statistics_table.setHorizontalHeaderLabels( ['Width', 'Height', 'Count', 'Aspect ratio', 'Size utilization']) - self.statistics_table.setMinimumWidth(500) + self.statistics_table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch) + self.statistics_table.setMinimumWidth(600) + self.statistics_table.setMinimumHeight(100) self.statistics_table.setEditTriggers(QAbstractItemView.NoEditTriggers) self.statistics_table.itemDoubleClicked.connect(self.set_filter) grid_layout.addWidget(self.statistics_table, grid_row, 1, diff --git a/taggui/models/image_list_model.py b/taggui/models/image_list_model.py index d1f191a2..1afbec9c 100644 --- a/taggui/models/image_list_model.py +++ b/taggui/models/image_list_model.py @@ -17,6 +17,7 @@ from utils.image import Image, ImageMarking, Marking from utils.settings import DEFAULT_SETTINGS, settings from utils.utils import get_confirmation_dialog_reply, pluralize +import utils.target_dimension as target_dimension UNDO_STACK_SIZE = 32 @@ -115,6 +116,16 @@ def data(self, index, role=None) -> Image | str | QIcon | QSize: # Scale the dimensions to the image width. return QSize(self.image_list_image_width, int(self.image_list_image_width * height / width)) + if role == Qt.ToolTipRole: + path = image.path.relative_to(settings.value('directory_path', type=str)) + dimensions = f'{image.dimensions[0]}:{image.dimensions[1]}' + if not image.target_dimension: + if image.crop: + image.target_dimension = target_dimension.get(image.crop.size()) + else: + image.target_dimension = target_dimension.get(QSize(*image.dimensions)) + target = f'{image.target_dimension.width()}:{image.target_dimension.height()}' + return f'{path}\n{dimensions} 🠮 {target}' def load_directory(self, directory_path: Path): self.images.clear() @@ -197,7 +208,9 @@ def load_directory(self, directory_path: Path): markings = meta.get('markings') if markings and type(markings) is list: for marking in markings: - marking = Marking(marking.get('label'), ImageMarking[marking.get('type')], QRect(*marking.get('rect'))) + marking = Marking(marking.get('label'), + ImageMarking[marking.get('type')], + QRect(*marking.get('rect'))) image.markings.append(marking) else: error_messages.append(f'Invalid version ' From e58713bbdbb958344c37b7995e645ab9875ab340 Mon Sep 17 00:00:00 2001 From: StableLlama Date: Sun, 16 Mar 2025 19:07:26 +0100 Subject: [PATCH 37/82] Implement undo for markings. Add the marking and cropping section to the readme. Little bugfixes. --- README.md | 68 ++++- taggui/dialogs/export_dialog.py | 60 ++--- taggui/models/image_list_model.py | 36 +-- taggui/utils/enums.py | 7 + taggui/utils/grid.py | 8 +- taggui/utils/rect.py | 155 +++++++++++ taggui/utils/settings.py | 2 +- taggui/widgets/image_viewer.py | 412 +++++++++++++----------------- taggui/widgets/main_window.py | 6 + 9 files changed, 467 insertions(+), 287 deletions(-) create mode 100644 taggui/utils/rect.py diff --git a/README.md b/README.md index 314da3b6..f2447681 100644 --- a/README.md +++ b/README.md @@ -139,7 +139,7 @@ apply: caption. For example, images with the tag `orange cat` or the tag `catastrophe`. - `marking`: Images that contain at least one marking with this label. It - doesn't matther whether it is a hint, include or exclude marking. + doesn't matter whether it is a _hint_, _include_ or _exclude_ marking. - `name`: Images that contain the filter term in the file name - `name:cat` will match images such as `cat-1.jpg` or `large_cat.png`. - `path`: Images that contain the filter term in the full file path @@ -263,6 +263,66 @@ You can nest parentheses and operators to create arbitrarily complex filters. The `Edit` menu contains additional features for batch tag operations, such as `Find and Replace` (`Ctrl`+`R`) and `Batch Reorder Tags` (`Ctrl`+`B`). +## Cropping and masking with markings + +Next to tagging images with words and text, taggui supports visual tagging, +called _marking_. There are different types of marking and except the `crop` +they can be changed into each other. + +All markings are marking the pixels inside the border 🞑, not any pixels below +the border. + +### Crop + +The _crop_, shown with a blue border 🞑, defines +the part of the image that will be exported. Depending on the export settings +likely a bucketing is configured. When the cropped area doesn't exactly fit +into a bucket as defined by the _Bucket resolution size_ and the _Bucket fitting +strategy_, it might be necessary to crop even more. This additional cropped +area is shown by a semitransparent red overlay. + +### Hint + +A _hint_, shown with a gray border 🞑, is just +a hint and has no effect on exporting the image. A _hint_ has a label where +you can give it a name and which you can use for filtering images which contain +the given marking. +A _hint_ can be changed in an _exclude_ or an _include_. + +### Exclude + +An _exclude_, shown with a red border 🞑, is +an area that is guaranteed to be masked (made transparent) when the image is +exported. +When _Latent size_ and _Quantize alpha channel_ are set and the _exclude_ area +doesn't fit, the mask will be grown to make sure that no excluded pixel will +stay unmasked. +An _exclude_ can be changed in an _include_ or a _hint_. + +### Include + +An _include_, shown with a green border 🞑, is +an area that is included when the image is exported. +When no _include_ is set, the full image (of course respecting the _crop_) is +included. +When an _include_ and an _exclude_ are overlapping, the _exclude_ takes +precedence. +And when _Latent size_ and _Quantize alpha channel_ are set and the _include_ +area doesn't fit, the mask will be shrunken to make sure that only included +pixels will stay unmasked. +An _include_ can be changed in a _hint_ or an _include_. + +### Working with markings + +Markings can be created by the use of the toolbar or by holding the `ctrl` key +to create a _hint_ or with `ctrl + alt` to create an _exclude_. +The position and size can be changed by dragging them to the desired place. +When the `shift` key is pressed during dragging the current part is snapped +to the next position that fits the current _export_ settings. +The marking label can be edited by clicking on it. +And the type can be changed in the toolbar or with a right mouse button click on +the marking. + ## Export Exporting the images to a directory allows different options. By choosing the @@ -288,14 +348,14 @@ The bucket size the training tool is using. `Latent size`: The size of one latent space pixel in image pixels. -`Quantisize alpha channel`: -When exporting with include or exclude markings in an image fortmat that +`Quantize alpha channel`: +When exporting with _include_ or _exclude_ markings in an image format that supports alpha masks (all, but not the classic JPEG) you can make sure that the masks are aligned to the latent pixels that the trainer is using for masked training. `Masked content`: -When exporting with include or exclude markings in an image fortmat that +When exporting with _include_ or _exclude_ markings in an image format that supports alpha masks (all, but not the classic JPEG) you can change the content that is invisible due to the mask. It is known that some masked content can slightly bleed through during the diff --git a/taggui/dialogs/export_dialog.py b/taggui/dialogs/export_dialog.py index 0cf134a7..88322633 100644 --- a/taggui/dialogs/export_dialog.py +++ b/taggui/dialogs/export_dialog.py @@ -1,19 +1,16 @@ -from enum import Enum from collections import defaultdict import os import io from math import ceil, floor from pathlib import Path -import shutil import numpy as np from PySide6.QtCore import QRect, QSize, Qt, Slot from PySide6.QtGui import QColorSpace from PySide6.QtWidgets import (QWidget, QDialog, QFileDialog, QGridLayout, - QHeaderView, QLabel, QLineEdit, QPushButton, - QTableWidget, QTableWidgetItem, QProgressBar, - QMessageBox, QVBoxLayout, QHBoxLayout, - QSizePolicy, QAbstractItemView) + QHeaderView, QLabel, QPushButton, QTableWidget, + QTableWidgetItem, QProgressBar, QMessageBox, + QVBoxLayout, QHBoxLayout, QAbstractItemView) from PIL import Image, ImageFilter, ImageCms from utils.settings import DEFAULT_SETTINGS, settings @@ -122,14 +119,14 @@ def __init__(self, parent, image_list: ImageList): 'Size of one latent space pixel in image space pixels') latent_layout.addWidget(self.latent_size_spin_box, Qt.AlignmentFlag.AlignLeft) - latent_layout.addWidget(QLabel('Quantisize alpha channel'), + latent_layout.addWidget(QLabel('Quantize alpha channel'), Qt.AlignmentFlag.AlignRight) - self.quantisize_alpha_check_box = SettingsBigCheckBox(key='export_quantisize_alpha') - self.quantisize_alpha_check_box.setToolTip( + self.quantize_alpha_check_box = SettingsBigCheckBox(key='export_quantize_alpha') + self.quantize_alpha_check_box.setToolTip( 'Align the masks due to include and exclude marking with the\n' 'latent space pixels.\n' 'Only available when the output format supports an alpha channel.') - latent_layout.addWidget(self.quantisize_alpha_check_box, + latent_layout.addWidget(self.quantize_alpha_check_box, Qt.AlignmentFlag.AlignLeft) latent_layout.addWidget(QLabel('Masked content'), Qt.AlignmentFlag.AlignRight) @@ -345,22 +342,22 @@ def format_change(self, export_format: ExportFormat, do_value_change: bool = Tru if export_format == ExportFormat.JPG: self.quality_spin_box.setValue(75) if do_value_change else 0 self.quality_spin_box.setEnabled(True) - self.quantisize_alpha_check_box.setEnabled(False) + self.quantize_alpha_check_box.setEnabled(False) self.masked_content_combo_box.setEnabled(False) if export_format == ExportFormat.JPGXL: self.quality_spin_box.setValue(100) if do_value_change else 0 self.quality_spin_box.setEnabled(True) - self.quantisize_alpha_check_box.setEnabled(True) + self.quantize_alpha_check_box.setEnabled(True) self.masked_content_combo_box.setEnabled(True) elif export_format == ExportFormat.PNG: self.quality_spin_box.setValue(100) if do_value_change else 0 self.quality_spin_box.setEnabled(False) - self.quantisize_alpha_check_box.setEnabled(True) + self.quantize_alpha_check_box.setEnabled(True) self.masked_content_combo_box.setEnabled(True) elif export_format == ExportFormat.WEBP: self.quality_spin_box.setValue(80) if do_value_change else 0 self.quality_spin_box.setEnabled(True) - self.quantisize_alpha_check_box.setEnabled(True) + self.quantize_alpha_check_box.setEnabled(True) self.masked_content_combo_box.setEnabled(True) @Slot() @@ -540,7 +537,7 @@ def do_export(self): resolution = settings.value('export_resolution', type=int) bucket_res = settings.value('export_bucket_res_size', type=int) latent_size = settings.value('export_latent_size', type=int) - quantisize_alpha = settings.value('export_quantisize_alpha', type=bool) + quantize_alpha = settings.value('export_quantize_alpha', type=bool) masked_content = settings.value('export_masked_content', type=str) export_format = settings.value('export_format', type=str) quality = settings.value('export_quality', type=int) @@ -614,7 +611,7 @@ def do_export(self): alpha = Image.new('L', image_file.size, 0) else: alpha = image_file.getchannel('A') - if not quantisize_alpha: + if not quantize_alpha: alpha.paste(255, marking.rect.adjusted(0,0,1,1).getCoords()) image_file.putalpha(alpha) @@ -627,59 +624,58 @@ def do_export(self): alpha = Image.new('L', image_file.size, 255) else: alpha = image_file.getchannel('A') - if not quantisize_alpha: + if not quantize_alpha: alpha.paste(0, marking.rect.adjusted(0,0,1,1).getCoords()) image_file.putalpha(alpha) - if image_entry.crop == None: + if image_entry.crop is None: grid = Grid(QRect(0, 0, *image_file.size)) else: grid = Grid(image_entry.crop) visible = grid.visible cropped_image = image_file.crop(visible.adjusted(0,0,1,1).getCoords()) - if not grid.is_visible_equal_sceen_size(): + if not grid.is_visible_equal_screen_size(): # resize with the best method available - #resized_image = cropped_image.resize((new_width, new_height), Image.LANCZOS) resized_image = cropped_image.resize(grid.target.toTuple(), Image.LANCZOS) # followed by a slight sharpening as it should be done - sharpend_image = resized_image.filter( + sharpened_image = resized_image.filter( ImageFilter.UnsharpMask(radius = 0.5, percent = 50, threshold = 0)) else: - sharpend_image = cropped_image + sharpened_image = cropped_image # crop to the desired size - current_width, current_height = sharpend_image.size + current_width, current_height = sharpened_image.size crop_width = floor((current_width - image_entry.target_dimension.width()) / 2) crop_height = floor((current_height - image_entry.target_dimension.height()) / 2) - cropped_image = sharpend_image.crop((crop_width, crop_height, + cropped_image = sharpened_image.crop((crop_width, crop_height, crop_width + image_entry.target_dimension.width(), crop_height + image_entry.target_dimension.height())) if export_can_alpha: alpha = cropped_image.getchannel('A') - if quantisize_alpha: - if image_entry.crop == None: + if quantize_alpha: + if image_entry.crop is None: crop = QRect(0, 0, *image_entry.dimensions) else: crop = image_entry.crop for marking in image_entry.markings: if marking.type == ImageMarking.INCLUDE: rect = QRect(grid.map(marking.rect.topLeft(), ceil), - grid.map(marking.rect.bottomRight(), floor)) + grid.map(marking.rect.adjusted(0,0,1,1).bottomRight(), floor)) alpha.paste(255, rect.getCoords()) for marking in image_entry.markings: if marking.type == ImageMarking.EXCLUDE: rect = QRect(grid.map(marking.rect.topLeft(), floor), - grid.map(marking.rect.bottomRight(), ceil)) + grid.map(marking.rect.adjusted(0,0,1,1).bottomRight(), ceil)) alpha.paste(0, rect.getCoords()) replacement = None if masked_content in [MaskedContent.BLUR, MaskedContent.BLUR_NOISE]: replacement = cropped_image.filter(ImageFilter.GaussianBlur(10)) elif masked_content in [MaskedContent.GREY, MaskedContent.GREY_NOISE]: - # 126 is an 18% grey, i.e. the neutral grey, for sRGB - # as it's masked there's no need to go into detail about - # different color spaces. + # 126 is an 18% gray, i.e. the neutral gray, for sRGB. + # As it's masked anyway, there's no need to go into detail + # about different color spaces. replacement = Image.new('RGB', cropped_image.size, (126, 126, 126)) elif masked_content == MaskedContent.BLACK: replacement = Image.new('RGB', cropped_image.size, (0, 0, 0)) @@ -726,7 +722,6 @@ def do_export(self): def get_image_list(self): image_list_view = self.image_list.list_view if settings.value('export_filter') == ExportFilter.FILTERED: - images = image_list_view.proxy_image_list_model.sourceModel() image_list = [] for row in range(image_list_view.proxy_image_list_model.sourceModel().rowCount()): source_index = image_list_view.proxy_image_list_model.sourceModel().index(row, 0) @@ -734,7 +729,6 @@ def get_image_list(self): if proxy_index.isValid(): image_list.append(source_index.data(Qt.ItemDataRole.UserRole)) elif settings.value('export_filter') == ExportFilter.SELECTED: - images = image_list_view.proxy_image_list_model.sourceModel() image_list = [image_index.data(Qt.ItemDataRole.UserRole) for image_index in image_list_view.get_selected_image_indices()] else: # ExportFilter.NONE diff --git a/taggui/models/image_list_model.py b/taggui/models/image_list_model.py index 1afbec9c..b87e39ce 100644 --- a/taggui/models/image_list_model.py +++ b/taggui/models/image_list_model.py @@ -39,7 +39,7 @@ def get_file_paths(directory_path: Path) -> set[Path]: @dataclass class HistoryItem: action_name: str - tags: list[list[str]] + tags: list[dict[str, list[str] | QRect | None | list[Marking]]] should_ask_for_confirmation: bool @@ -94,8 +94,8 @@ def data(self, index, role=None) -> Image | str | QIcon | QSize: else: crop = QRect(QPoint(0, 0), image_reader.size()) if crop.height() > crop.width()*3: - # keep it sane, higher than 3x the width doesn't make sense - crop.setTop((crop.height() - crop.width()*3)/2) # center crop + # keep it reasonable, higher than 3x the width doesn't make sense + crop.setTop((crop.height() - crop.width()*3)//2) # center crop crop.setHeight(crop.width()*3) image_reader.setClipRect(crop) pixmap = QPixmap.fromImageReader(image_reader).scaledToWidth( @@ -103,7 +103,6 @@ def data(self, index, role=None) -> Image | str | QIcon | QSize: Qt.TransformationMode.SmoothTransformation) thumbnail = QIcon(pixmap) image.thumbnail = thumbnail - self.dataChanged.emit(index, index) return thumbnail if role == Qt.ItemDataRole.SizeHintRole: if image.thumbnail: @@ -116,7 +115,7 @@ def data(self, index, role=None) -> Image | str | QIcon | QSize: # Scale the dimensions to the image width. return QSize(self.image_list_image_width, int(self.image_list_image_width * height / width)) - if role == Qt.ToolTipRole: + if role == Qt.ItemDataRole.ToolTipRole: path = image.path.relative_to(settings.value('directory_path', type=str)) dimensions = f'{image.dimensions[0]}:{image.dimensions[1]}' if not image.target_dimension: @@ -230,7 +229,9 @@ def load_directory(self, directory_path: Path): def add_to_undo_stack(self, action_name: str, should_ask_for_confirmation: bool): """Add the current state of the image tags to the undo stack.""" - tags = [image.tags.copy() for image in self.images] + tags = [{'tags': image.tags.copy(), + 'crop': QRect(image.crop) if image.crop is not None else None, + 'markings': image.markings.copy()} for image in self.images] self.undo_stack.append(HistoryItem(action_name, tags, should_ask_for_confirmation)) self.redo_stack.clear() @@ -250,15 +251,12 @@ def write_image_tags_to_disk(self, image: Image): def write_meta_to_disk(self, image: Image): does_exist = image.path.with_suffix('.json').exists() - meta = {'version': 1} - if image.crop != None: + meta: dict[str, any] = {'version': 1} + if image.crop is not None: meta['crop'] = image.crop.getRect() - markings: list[dict[str, any]] = [] - for marking in image.markings: - markings.append({'label': marking.label, + meta['markings'] = [{'label': marking.label, 'type': marking.type.name, - 'rect': marking.rect.getRect()}) - meta['markings'] = markings + 'rect': marking.rect.getRect()} for marking in image.markings] if does_exist or len(meta.keys()) > 1: try: with image.path.with_suffix('.json').open('w', encoding='UTF-8') as meta_file: @@ -290,17 +288,23 @@ def restore_history_tags(self, is_undo: bool): if reply != QMessageBox.StandardButton.Yes: return source_stack.pop() - tags = [image.tags for image in self.images] + tags = [{'tags': image.tags.copy(), + 'crop': QRect(image.crop) if image.crop is not None else None, + 'markings': image.markings.copy()} for image in self.images] destination_stack.append(HistoryItem( history_item.action_name, tags, history_item.should_ask_for_confirmation)) changed_image_indices = [] for image_index, (image, history_image_tags) in enumerate( zip(self.images, history_item.tags)): - if image.tags == history_image_tags: + if (image.tags == history_image_tags['tags'] and + image.crop == history_image_tags['crop'] and + image.markings == history_image_tags['markings']): continue changed_image_indices.append(image_index) - image.tags = history_image_tags + image.tags = history_image_tags['tags'] + image.crop = history_image_tags['crop'] + image.markings = history_image_tags['markings'] self.write_image_tags_to_disk(image) if changed_image_indices: self.dataChanged.emit(self.index(changed_image_indices[0]), diff --git a/taggui/utils/enums.py b/taggui/utils/enums.py index 4a1b49e4..2f960776 100644 --- a/taggui/utils/enums.py +++ b/taggui/utils/enums.py @@ -25,11 +25,13 @@ class CaptionDevice(str, Enum): GPU = 'GPU if available' CPU = 'CPU' + class ExportFilter(str, Enum): NONE = 'All images' FILTERED = 'Filtered images' SELECTED = 'Selected images' + Presets = { 'manual': (0, 0, 1, '1:1, 2:1, 3:2, 4:3, 16:9, 21:9'), 'Direct feed through': (0, 1, 1, '1:1, 2:1, 3:2, 4:3, 16:9, 21:9'), @@ -37,6 +39,7 @@ class ExportFilter(str, Enum): 'SDXL, SD3, Flux': (1024, 64, 8, '1024:1024, 1408:704, 1216:832, 1152:896, 1344:768, 1536:640') } + class MaskedContent(str, Enum): ORIGINAL = 'original' BLUR = 'blur' @@ -46,12 +49,14 @@ class MaskedContent(str, Enum): BLACK = 'black' WHITE = 'white' + class ExportFormat(str, Enum): JPG = '.jpg - JPEG' JPGXL = '.jxl - JPEG XL' PNG = '.png - PNG' WEBP = '.webp - WEBP' + ExportFormatDict = { ExportFormat.JPG: 'jpeg', ExportFormat.JPGXL: 'jxl', @@ -59,6 +64,7 @@ class ExportFormat(str, Enum): ExportFormat.WEBP: 'webp' } + class IccProfileList(str, Enum): SRgb = 'sRGB' SRgbLinear = 'sRGB (linear gamma)' @@ -69,6 +75,7 @@ class IccProfileList(str, Enum): Bt2100Pq = 'BT.2100(PQ)' Bt2100Hlg = 'BT.2100 (HLG)' + class BucketStrategy(str, Enum): CROP = 'crop' SCALE = 'scale' diff --git a/taggui/utils/grid.py b/taggui/utils/grid.py index 03cc342a..7273251b 100644 --- a/taggui/utils/grid.py +++ b/taggui/utils/grid.py @@ -1,11 +1,11 @@ from math import ceil, floor -from PySide6.QtCore import (QPoint, QPointF, QRect, QRectF, QSize, QSizeF) +from PySide6.QtCore import (QPoint, QPointF, QRect, QSize) from utils.enums import BucketStrategy from utils.settings import settings import utils.target_dimension as target_dimension class Grid: - """Span a grid inside a the sceen. + """Span a grid inside the screen. The screen is adjusted according to bucket strategy and scaled to the target dimension and then the grid respects the latent size. @@ -60,7 +60,7 @@ def update(self, screen: QRect | None = None): self.scale_x = self.target.width() / self.visible.width() self.scale_y = self.target.height() / self.visible.height() - def is_visible_equal_sceen_size(self) -> bool: + def is_visible_equal_screen_size(self) -> bool: return self.screen.size() == self.visible def map_raw(self, point: QPoint) -> QPointF: @@ -81,7 +81,7 @@ def map(self, point: QPoint, method = round) -> QPoint: method(raw.y()/latent_size)*latent_size) def snap(self, point: QPoint, method = round) -> QPointF: - """Align the point to the closes position on the grid but in + """Align the point to the closest position on the grid but in screen coordinates. """ assert isinstance(point, QPoint) diff --git a/taggui/utils/rect.py b/taggui/utils/rect.py new file mode 100644 index 00000000..4d01a402 --- /dev/null +++ b/taggui/utils/rect.py @@ -0,0 +1,155 @@ +from enum import Enum +from math import floor, ceil + +from PySide6.QtCore import QPoint, QRect, QSize, QPointF, QRectF + + +class RectPosition(str, Enum): + TL = 'top left' + TOP = 'top' + TR = 'top right' + RIGHT = 'right' + BR = 'bottom right' + BOTTOM = 'bottom' + BL = 'bottom left' + LEFT = 'left' + NONE = 'none' + +def get_rect_position(left: bool, right: bool, top: bool, bottom: bool) -> RectPosition: + if top: + if left: + return RectPosition.TL + elif right: + return RectPosition.TR + return RectPosition.TOP + elif bottom: + if left: + return RectPosition.BL + elif right: + return RectPosition.BR + return RectPosition.BOTTOM + if left: + return RectPosition.LEFT + elif right: + return RectPosition.RIGHT + + return RectPosition.NONE + +def flip_rect_position(pos: RectPosition, h_flip: bool, v_flip: bool) -> RectPosition: + if pos == RectPosition.NONE: + return RectPosition.NONE + + if pos == RectPosition.TL or pos == RectPosition.TOP or pos == RectPosition.TR: + v = 2 if v_flip else 0 + elif pos == RectPosition.LEFT or pos == RectPosition.RIGHT: + v = 1 + else: + v = 0 if v_flip else 2 + + if pos == RectPosition.TL or pos == RectPosition.LEFT or pos == RectPosition.BL: + h = 2 if h_flip else 0 + elif pos == RectPosition.TOP or pos == RectPosition.BOTTOM: + h = 1 + else: + h = 0 if h_flip else 2 + + return { + 0: RectPosition.TL, 1: RectPosition.TOP, 2: RectPosition.TR, + 10: RectPosition.LEFT, 12: RectPosition.RIGHT, + 20: RectPosition.BL, 21: RectPosition.BOTTOM, 22: RectPosition.BR, + }[h+10*v] + +def change_rect(rect: QRect, rect_pos: RectPosition, pos: QPoint) -> QRect: + """Change `rect` to move `rect_pos` at `pos`.""" + if rect_pos == RectPosition.TL: + rect.setTopLeft(pos) + elif rect_pos == RectPosition.TOP: + rect.setTop(pos.y()) + elif rect_pos == RectPosition.TR: + rect.setTopRight(pos) + elif rect_pos == RectPosition.RIGHT: + rect.setRight(pos.x() - 1) + elif rect_pos == RectPosition.BR: + rect.setBottomRight(pos - QPoint(1, 1)) + elif rect_pos == RectPosition.BOTTOM: + rect.setBottom(pos.y() - 1) + elif rect_pos == RectPosition.BL: + rect.setBottomLeft(pos) + elif rect_pos == RectPosition.LEFT: + rect.setLeft(pos.x()) + return rect + +def change_rectF(rect: QRectF, rect_pos: RectPosition, pos: QPointF) -> QRectF: + """Change `rect` to move `rect_pos` at `pos`.""" + if rect_pos == RectPosition.TL: + rect.setTopLeft(pos) + elif rect_pos == RectPosition.TOP: + rect.setTop(pos.y()) + elif rect_pos == RectPosition.TR: + rect.setTopRight(pos) + elif rect_pos == RectPosition.RIGHT: + rect.setRight(pos.x()) + elif rect_pos == RectPosition.BR: + rect.setBottomRight(pos) + elif rect_pos == RectPosition.BOTTOM: + rect.setBottom(pos.y()) + elif rect_pos == RectPosition.BL: + rect.setBottomLeft(pos) + elif rect_pos == RectPosition.LEFT: + rect.setLeft(pos.x()) + return rect + +def change_rect_round(rect: QRect, rect_pos: RectPosition, pos: QPointF, grow: bool) -> QRect: + """Change `rect` to move `rect_pos` at `pos` and round by growing or shrinking the rect as `grow` demands.""" + round_tl = floor if grow else ceil + round_br = ceil if grow else floor + + if rect_pos == RectPosition.TL: + rect.setTopLeft(QPoint(round_tl(pos.x()), round_tl(pos.y()))) + elif rect_pos == RectPosition.TOP: + rect.setTop(round_tl(pos.y())) + elif rect_pos == RectPosition.TR: + rect.setTopRight(QPoint(round_br(pos.x()), round_tl(pos.y()))) + elif rect_pos == RectPosition.RIGHT: + rect.setRight(round_br(pos.x())) + elif rect_pos == RectPosition.BR: + rect.setBottomRight(QPoint(round_br(pos.x()), round_br(pos.y()))) + elif rect_pos == RectPosition.BOTTOM: + rect.setBottom(round_br(pos.y())) + elif rect_pos == RectPosition.BL: + rect.setBottomLeft(QPoint(round_tl(pos.x()), round_br(pos.y()))) + elif rect_pos == RectPosition.LEFT: + rect.setLeft(round_tl(pos.x())) + return rect + +def change_rect_to_match_size(rect: QRect, rect_pos: RectPosition, size: QSize) -> QRect: + """Change the `rect` at place `rect_pos` so that the size matches `size`. + + Moving one side will ignore the value in the size of the perpendicular side. + """ + rect_new = QRect(rect) + if rect_pos == RectPosition.TL: + rect_new.setSize(size) + rect_new.moveBottomRight(rect.bottomRight()) + elif rect_pos == RectPosition.TOP: + rect_new.setHeight(size.height()) + rect_new.moveBottom(rect.bottom()) + elif rect_pos == RectPosition.TR: + rect_new.setSize(size) + rect_new.moveBottomLeft(rect.bottomLeft()) + elif rect_pos == RectPosition.RIGHT: + rect_new.setWidth(size.width()) + rect_new.moveLeft(rect.left()) + elif rect_pos == RectPosition.BR: + rect_new.setSize(size) + rect_new.moveTopLeft(rect.topLeft()) + elif rect_pos == RectPosition.BOTTOM: + rect_new.setHeight(size.height()) + rect_new.moveTop(rect.top()) + elif rect_pos == RectPosition.BL: + rect_new.setSize(size) + rect_new.moveTopRight(rect.topRight()) + elif rect_pos == RectPosition.LEFT: + rect_new.setWidth(size.width()) + rect_new.moveRight(rect.right()) + return rect_new diff --git a/taggui/utils/settings.py b/taggui/utils/settings.py index b2a07a2d..5673df78 100644 --- a/taggui/utils/settings.py +++ b/taggui/utils/settings.py @@ -15,7 +15,7 @@ 'export_resolution': 1024, 'export_bucket_res_size': 64, 'export_latent_size': 8, - 'export_quantisize_alpha': True, + 'export_quantize_alpha': True, 'export_masked_content': 'blur + noise', 'export_preferred_sizes' : '1024:1024, 1408:704, 1216:832, 1152:896, 1344:768, 1536:640', 'export_upscaling': False, diff --git a/taggui/widgets/image_viewer.py b/taggui/widgets/image_viewer.py index 6c31f9f6..6d5fd847 100644 --- a/taggui/widgets/image_viewer.py +++ b/taggui/widgets/image_viewer.py @@ -1,11 +1,9 @@ -from enum import Enum from math import ceil, floor -from pathlib import Path -from PySide6.QtCore import (QEvent, QModelIndex, QObject, QPersistentModelIndex, - QPoint, QPointF, QRect, QRectF, QSize, QSizeF, Qt, - Signal, Slot) +from PySide6.QtCore import (QModelIndex, QPersistentModelIndex, QPoint, QPointF, + QRect, QRectF, QSize, Qt, Signal, Slot) from PySide6.QtGui import (QAction, QActionGroup, QCursor, QColor, QIcon, - QPainter, QPainterPath, QPen, QPixmap, QTransform) + QPainter, QPainterPath, QPen, QPixmap, QTransform, + QMouseEvent) from PySide6.QtWidgets import (QGraphicsItem, QGraphicsLineItem, QGraphicsPixmapItem, QGraphicsRectItem, QGraphicsTextItem, QGraphicsScene, QGraphicsView, @@ -15,7 +13,9 @@ from utils.image import Image, ImageMarking, Marking import utils.target_dimension as target_dimension from utils.grid import Grid -from dialogs.export_dialog import BucketStrategy +from utils.rect import (RectPosition, flip_rect_position, change_rect, + change_rect_to_match_size) + # Grid for alignment to latent space grid = Grid(QRect(0, 0, 1, 1)) @@ -27,102 +27,20 @@ ImageMarking.EXCLUDE: Qt.red, } -class RectPosition(str, Enum): - TL = 'top left' - TOP = 'top' - TR = 'top right' - RIGHT = 'right' - BR = 'bottom right' - BOTTOM = 'bottom' - BL = 'bottom left' - LEFT = 'left' - NONE = 'none' - -def flip_rect_position(pos: RectPosition, h_flip: bool, v_flip: bool) -> RectPosition: - if pos == RectPosition.NONE: - return RectPosition.NONE - - if pos == RectPosition.TL or pos == RectPosition.TOP or pos == RectPosition.TR: - v = 2 if v_flip else 0 - elif pos == RectPosition.LEFT or pos == RectPosition.RIGHT: - v = 1 - else: - v = 0 if v_flip else 2 - - if pos == RectPosition.TL or pos == RectPosition.LEFT or pos == RectPosition.BL: - h = 2 if h_flip else 0 - elif pos == RectPosition.TOP or pos == RectPosition.BOTTOM: - h = 1 - else: - h = 0 if h_flip else 2 - - return { - 0: RectPosition.TL, 1: RectPosition.TOP, 2: RectPosition.TR, - 10: RectPosition.LEFT, 12: RectPosition.RIGHT, - 20: RectPosition.BL, 21: RectPosition.BOTTOM, 22: RectPosition.BR, - }[h+10*v] - -def change_rect(rect: QRect, rect_pos: RectPosition, pos: QPoint) -> QRect: - if rect_pos == RectPosition.TL: - rect.setTopLeft(pos) - elif rect_pos == RectPosition.TOP: - rect.setTop(pos.y()) - elif rect_pos == RectPosition.TR: - rect.setTopRight(pos) - elif rect_pos == RectPosition.RIGHT: - rect.setRight(pos.x()) - elif rect_pos == RectPosition.BR: - rect.setBottomRight(pos) - elif rect_pos == RectPosition.BOTTOM: - rect.setBottom(pos.y()) - elif rect_pos == RectPosition.BL: - rect.setBottomLeft(pos) - elif rect_pos == RectPosition.LEFT: - rect.setLeft(pos.x()) - return rect - -def change_rect_to_match_size(rect: QRect, rect_pos: RectPosition, size: QSize) -> QRect: - """Change the `rect` at place `rect_pos` so that the size matches `size`. - - Moving a side will ignore the value in the size for the perpendicular side. - """ - rect_new = QRect(rect) - if rect_pos == RectPosition.TL: - rect_new.setSize(size) - rect_new.moveBottomRight(rect.bottomRight()) - elif rect_pos == RectPosition.TOP: - rect_new.setHeight(size.height()) - rect_new.moveBottom(rect.bottom()) - elif rect_pos == RectPosition.TR: - rect_new.setSize(size) - rect_new.moveBottomLeft(rect.bottomLeft()) - elif rect_pos == RectPosition.RIGHT: - rect_new.setWidth(size.width()) - rect_new.moveLeft(rect.left()) - elif rect_pos == RectPosition.BR: - rect_new.setSize(size) - rect_new.moveTopLeft(rect.topLeft()) - elif rect_pos == RectPosition.BOTTOM: - rect_new.setHeight(size.height()) - rect_new.moveTop(rect.top()) - elif rect_pos == RectPosition.BL: - rect_new.setSize(size) - rect_new.moveTopRight(rect.topRight()) - elif rect_pos == RectPosition.LEFT: - rect_new.setWidth(size.width()) - rect_new.moveRight(rect.right()) - return rect_new - def calculate_grid(content: QRect): global grid grid = Grid(content) -class RectItemSignal(QObject): - change = Signal(QGraphicsRectItem, name='rectChanged') - move = Signal(QRectF, RectPosition, name='rectIsMoving') +# Organization of Z value: +# 0: the image +# 1: the semi transparent include/exclude areas +# 2: the borders of the markings +# 3: the HUD +# 4: the currently manipulated marking +# 5: the cross hair lines class MarkingItem(QGraphicsRectItem): - # the halfed size of the pen in local coordinates to make sure it stays the + # the halved size of the pen in local coordinates to make sure it stays the # same during zooming pen_half_width = 1.0 # the minimal size of the active area in scene coordinates @@ -140,39 +58,41 @@ def __init__(self, rect: QRect, rect_type: ImageMarking, self.setFlag(QGraphicsItem.ItemIsSelectable, True) self.setFlag(QGraphicsItem.ItemSendsScenePositionChanges, True) self.setAcceptHoverEvents(True) - self.signal = RectItemSignal() self.rect_type = rect_type self.label: MarkingLabel | None = None self.color = marking_colors[rect_type] self.handle_selected = None self.mouse_press_pos = None self.mouse_press_rect = None + self.setZValue(2) + if rect_type in [ImageMarking.INCLUDE, ImageMarking.EXCLUDE]: + self.area = QGraphicsRectItem(self) + self.area.setFlag(QGraphicsItem.ItemStacksBehindParent) + self.area.setZValue(1) + area_color = QColor(self.color) + area_color.setAlpha(127) + self.area.setBrush(area_color) + self.area.setPen(Qt.NoPen) + self.move(False) + + def move(self, is_in_move): + if self.rect_type == ImageMarking.CROP: + if not is_in_move: + self.image_view.image_viewer.hud_item.setValues(self.rect(), self.handle_selected) + elif self.rect_type == ImageMarking.INCLUDE: + self.area.setRect(QRectF(grid.snap(self.rect().toRect().topLeft(), ceil), + grid.snap(self.rect().toRect().adjusted(0,0,1,1).bottomRight(), floor))) + elif self.rect_type == ImageMarking.EXCLUDE: + self.area.setRect(QRectF(grid.snap(self.rect().toRect().topLeft(), floor), + grid.snap(self.rect().toRect().adjusted(0,0,1,1).bottomRight(), ceil))) def handleAt(self, point: QPointF) -> RectPosition: handle_space = -min(self.pen_half_width - self.handle_half_size, 0)/self.zoom_factor - left = point.x() < self.rect().left() + handle_space - right = point.x() > self.rect().right() - handle_space - top = point.y() < self.rect().top() + handle_space - bottom = point.y() > self.rect().bottom() - handle_space - if top: - if left: - return RectPosition.TL - elif right: - return RectPosition.TR - return RectPosition.TOP - elif bottom: - if left: - return RectPosition.BL - elif right: - return RectPosition.BR - return RectPosition.BOTTOM - if left: - return RectPosition.LEFT - elif right: - return RectPosition.RIGHT - - return RectPosition.NONE + return get_rect_position(point.x() < self.rect().left() + handle_space, + point.x() > self.rect().right() - handle_space, + point.y() < self.rect().top() + handle_space, + point.y() > self.rect().bottom() - handle_space) def hoverMoveEvent(self, event): handle = self.handleAt(event.pos()) @@ -193,10 +113,13 @@ def mousePressEvent(self, event): self.handle_selected = self.handleAt(event.pos()) if (event.button() == Qt.MouseButton.LeftButton and self.handle_selected != RectPosition.NONE): + self.image_view.image_viewer.proxy_image_index.model().sourceModel().add_to_undo_stack( + action_name=f'Change marking geometry', should_ask_for_confirmation=False) self.mouse_press_pos = event.pos() self.mouse_press_scene_pos = event.scenePos() self.mouse_press_rect = self.rect() - self.signal.move.emit(self.rect(), self.handle_selected) + self.setZValue(4) + self.move(False) else: event.ignore() @@ -205,11 +128,10 @@ def mouseMoveEvent(self, event): if ((event.modifiers() & Qt.KeyboardModifier.ShiftModifier) == Qt.KeyboardModifier.ShiftModifier): if self.rect_type == ImageMarking.CROP: - pos_quantizised = event.pos().toPoint() - pos_quantizised_pre = pos_quantizised + pos_quantized = event.pos().toPoint() rect_pre = change_rect(self.rect().toRect(), self.handle_selected, - pos_quantizised) + pos_quantized) target = target_dimension.get(rect_pre.size()) if rect_pre.height() * target.width() / rect_pre.width() < target.height(): # too wide scale = rect_pre.height() / target.height() @@ -232,15 +154,18 @@ def mouseMoveEvent(self, event): self.handle_selected, target_size) else: - pos_quantizised = grid.snap(event.pos().toPoint()).toPoint() - rect = change_rect(self.rect().toRect(), + rect = change_rect(self.rect(), self.handle_selected, - pos_quantizised) + event.pos()) + rect = QRectF(grid.snap(rect.toRect().topLeft(), floor), + grid.snap(rect.toRect().bottomRight(), ceil)) + rect = QRect(QPoint(ceil(rect.topLeft().x()),ceil(rect.topLeft().y())), + QPoint(floor(rect.bottomRight().x()),floor(rect.bottomRight().y()))) else: - pos_quantizised = event.pos().toPoint() + pos_quantized = event.pos().toPoint() rect = change_rect(self.rect().toRect(), self.handle_selected, - pos_quantizised) + pos_quantized) self.handle_selected = flip_rect_position(self.handle_selected, rect.width() < 0, @@ -253,18 +178,18 @@ def mouseMoveEvent(self, event): self.setRect(rect) self.size_changed() - self.signal.move.emit(self.rect(), self.handle_selected) + self.move(True) super().mouseMoveEvent(event) def mouseReleaseEvent(self, event): if self.handle_selected: self.handle_selected = RectPosition.NONE - self.signal.move.emit(self.rect(), self.handle_selected) - self.signal.change.emit(self) + self.move(False) + self.image_view.image_viewer.marking_changed(self) + self.setZValue(2) super().mouseReleaseEvent(event) def paint(self, painter, option, widget=None): - bucket_strategy = settings.value('export_bucket_strategy', type=str) if self.rect_type == ImageMarking.CROP: painter.setPen(Qt.NoPen) painter.setBrush(QColor(255, 0, 0, 127)) @@ -272,16 +197,6 @@ def paint(self, painter, option, widget=None): path.addRect(self.rect()) path.addRect(grid.visible) painter.drawPath(path) - elif self.rect_type == ImageMarking.INCLUDE and self.show_marking_latent: - painter.setPen(Qt.NoPen) - painter.setBrush(QColor(0, 255, 0, 127)) - painter.drawRect(QRectF(grid.snap(self.rect().toRect().topLeft(), ceil), - grid.snap(self.rect().toRect().bottomRight(), floor))) - elif self.rect_type == ImageMarking.EXCLUDE and self.show_marking_latent: - painter.setPen(Qt.NoPen) - painter.setBrush(QColor(255, 0, 0, 127)) - painter.drawRect(QRectF(grid.snap(self.rect().toRect().topLeft(), floor), - grid.snap(self.rect().toRect().bottomRight(), ceil))) pen_half_width = self.pen_half_width / self.zoom_factor pen = QPen(self.color, 2*pen_half_width, Qt.SolidLine, Qt.RoundCap, @@ -310,9 +225,6 @@ def boundingRect(self): adjust = (self.pen_half_width + max(self.pen_half_width, self.handle_half_size))/self.zoom_factor bbox = self.rect().adjusted(-adjust, -adjust, adjust, adjust) - if self.rect_type == ImageMarking.EXCLUDE: - bbox = bbox.united(QRectF(grid.snap(self.rect().toRect().topLeft(), floor), - grid.snap(self.rect().toRect().bottomRight(), ceil))) return bbox def size_changed(self): @@ -324,13 +236,14 @@ def size_changed(self): self.adjust_layout() def adjust_layout(self): - if self.label != None: + if self.label is not None: self.label.changeZoom(self.zoom_factor) pen_half_width = self.pen_half_width / self.zoom_factor if self.rect().y() > self.label.boundingRect().height(): self.label.setPos(self.rect().adjusted( -2 * pen_half_width, - -pen_half_width - self.label.boundingRect().height(), + -1.8*pen_half_width + - self.label.boundingRect().height() / self.zoom_factor, 0, 0).topLeft()) self.label.parentItem().setRect(self.label.sceneBoundingRect()) else: @@ -369,9 +282,7 @@ def insertFromMimeData(self, source): self.parentItem().setRect(self.sceneBoundingRect()) def changeZoom(self, zoom_factor): - font = self.font() - font.setPointSizeF(10 / zoom_factor) - self.setFont(font) + self.setScale(1/zoom_factor) self.parentItem().setRect(self.sceneBoundingRect()) @@ -383,6 +294,8 @@ def __init__(self, boundingRect: QRect, parent=None): self._boundingRect = boundingRect self.rect = QRectF(0, 0, 1, 1) self.path = QPainterPath() + self.setCacheMode(QGraphicsItem.DeviceCoordinateCache) + self.setZValue(3) @Slot(QRectF, RectPosition) def setValues(self, rect: QRectF, pos: RectPosition): @@ -413,45 +326,62 @@ def setValues(self, rect: QRectF, pos: RectPosition): self.update() - def add_line_limit_td(self, x: int, lr: int): + def add_line_limit_td(self, x: float, lr: int): width = settings.value('export_resolution', type=int)**2 / self.rect.height() + res_size = max(settings.value('export_bucket_res_size', type=int), 1) self.path.moveTo(x + lr * width, self.rect.y() ) self.path.lineTo(x + lr * width, self.rect.y() + self.rect.height()) for ar in target_dimension.get_preferred_sizes(): + s = max(res_size / ar[0], res_size / ar[1]) f = max(self._boundingRect.width() / ar[0], self._boundingRect.height() / ar[1], 2) - self.path.moveTo(x + lr * ar[0] , self.rect.y() + ar[1] ) + self.path.moveTo(x + lr * ar[0] * s, self.rect.y() + ar[1] * s) self.path.lineTo(x + lr * ar[0] * f, self.rect.y() + ar[1] * f) - self.path.moveTo(x + lr * ar[0] , self.rect.bottom() - ar[1] ) + self.path.moveTo(x + lr * ar[0] * s, self.rect.bottom() - ar[1] * s) self.path.lineTo(x + lr * ar[0] * f, self.rect.bottom() - ar[1] * f) - def add_line_limit_lr(self, y: int, td: int): + def add_line_limit_lr(self, y: float, td: int): height = settings.value('export_resolution', type=int)**2 / self.rect.width() + res_size = max(settings.value('export_bucket_res_size', type=int), 1) self.path.moveTo(self.rect.x(), y + td * height) self.path.lineTo(self.rect.x() + self.rect.width(), y + td * height) for ar in target_dimension.get_preferred_sizes(): + s = max(res_size / ar[0], res_size / ar[1]) f = max(self._boundingRect.width() / ar[0], self._boundingRect.height() / ar[1], 2) - self.path.moveTo(self.rect.x() + ar[0] , y + td * ar[1] ) + self.path.moveTo(self.rect.x() + ar[0] * s, y + td * ar[1] * s) self.path.lineTo(self.rect.x() + ar[0] * f, y + td * ar[1] * f) - self.path.moveTo(self.rect.right() - ar[0] , y + td * ar[1] ) + self.path.moveTo(self.rect.right() - ar[0] * s, y + td * ar[1] * s) self.path.lineTo(self.rect.right() - ar[0] * f, y + td * ar[1] * f) - def add_hyperbola_limit(self, pos: QPoint, lr: int, td: int): + def add_hyperbola_limit(self, pos: QPointF, lr: int, td: int): target_area = settings.value('export_resolution', type=int)**2 res_size = max(settings.value('export_bucket_res_size', type=int), 1) dx = res_size - self.path.moveTo(pos.x() + lr * dx, pos.y() + td * target_area / dx) + first = QPointF(pos.x() + lr * dx, pos.y() + td * target_area / dx) while dx * res_size <= target_area: - self.path.lineTo(pos.x() + lr * dx, pos.y() + td * target_area / dx) - dx = dx + 10 + p = QPointF(pos.x() + lr * dx, pos.y() + td * target_area / dx) + dx = dx + 50 + # add all points above the parent as well as one before and one after + if self.parentItem().contains(p): + if first: + self.path.moveTo(first) + first = None + self.path.lineTo(p) + else: + if first: + first = p + else: + self.path.lineTo(p) + break for ar in target_dimension.get_preferred_sizes(): + s = max(res_size / ar[0], res_size / ar[1]) f = max(self._boundingRect.width() / ar[0], self._boundingRect.height() / ar[1], 2) - self.path.moveTo(pos.x() + lr * ar[0] , pos.y() + td * ar[1] ) + self.path.moveTo(pos.x() + lr * ar[0] * s, pos.y() + td * ar[1] * s) self.path.lineTo(pos.x() + lr * ar[0] * f, pos.y() + td * ar[1] * f) def boundingRect(self): @@ -485,42 +415,41 @@ def __init__(self, scene, image_viewer): def showContextMenu(self, pos): scene_pos = self.mapToScene(pos) item = self.scene().itemAt(scene_pos, self.transform()) - if item is not None and item.handle_selected != RectPosition.NONE: + if isinstance(item, MarkingLabel): + item = item.parentItem().parentItem() + if isinstance(item, MarkingItem) and item.handle_selected != RectPosition.NONE: menu = QMenu() - if isinstance(item, MarkingLabel): - item = item.parentItem().parentItem() - if isinstance(item, MarkingItem): - if item.rect_type != ImageMarking.NONE: - if item.rect_type != ImageMarking.CROP: - marking_group = QActionGroup(menu) - change_to_hint_action = QAction('Hint', marking_group) - change_to_hint_action.setCheckable(True) - change_to_hint_action.setChecked(item.rect_type == ImageMarking.HINT) - change_to_hint_action.triggered.connect( - lambda: self.image_viewer.change_marking([item], ImageMarking.HINT)) - menu.addAction(change_to_hint_action) - change_to_exclude_action = QAction('Exclude', marking_group) - change_to_exclude_action.setCheckable(True) - change_to_exclude_action.setChecked(item.rect_type == ImageMarking.EXCLUDE) - change_to_exclude_action.triggered.connect( - lambda: self.image_viewer.change_marking([item], ImageMarking.EXCLUDE)) - menu.addAction(change_to_exclude_action) - change_to_include_action = QAction('Include', marking_group) - change_to_include_action.setCheckable(True) - change_to_include_action.setChecked(item.rect_type == ImageMarking.INCLUDE) - change_to_include_action.triggered.connect( - lambda: self.image_viewer.change_marking([item], ImageMarking.INCLUDE)) - menu.addAction(change_to_include_action) - menu.addSeparator() - delete_marking_action = QAction( - QIcon.fromTheme('edit-delete'), 'Delete', self) - delete_marking_action.triggered.connect( - lambda: self.image_viewer.delete_markings([item])) - menu.addAction(delete_marking_action) + if item.rect_type != ImageMarking.NONE: + if item.rect_type != ImageMarking.CROP: + marking_group = QActionGroup(menu) + change_to_hint_action = QAction('Hint', marking_group) + change_to_hint_action.setCheckable(True) + change_to_hint_action.setChecked(item.rect_type == ImageMarking.HINT) + change_to_hint_action.triggered.connect( + lambda: self.image_viewer.change_marking([item], ImageMarking.HINT)) + menu.addAction(change_to_hint_action) + change_to_exclude_action = QAction('Exclude', marking_group) + change_to_exclude_action.setCheckable(True) + change_to_exclude_action.setChecked(item.rect_type == ImageMarking.EXCLUDE) + change_to_exclude_action.triggered.connect( + lambda: self.image_viewer.change_marking([item], ImageMarking.EXCLUDE)) + menu.addAction(change_to_exclude_action) + change_to_include_action = QAction('Include', marking_group) + change_to_include_action.setCheckable(True) + change_to_include_action.setChecked(item.rect_type == ImageMarking.INCLUDE) + change_to_include_action.triggered.connect( + lambda: self.image_viewer.change_marking([item], ImageMarking.INCLUDE)) + menu.addAction(change_to_include_action) + menu.addSeparator() + delete_marking_action = QAction( + QIcon.fromTheme('edit-delete'), 'Delete', self) + delete_marking_action.triggered.connect( + lambda: self.image_viewer.delete_markings([item])) + menu.addAction(delete_marking_action) menu.exec(self.mapToGlobal(pos)) def clear_scene(self): - """Use this and not scene.clear() due to resource management.""" + """Use this and not scene().clear() due to resource management.""" self.insertion_mode = False self.horizontal_line = None self.vertical_line = None @@ -532,7 +461,9 @@ def set_insertion_mode(self, mode): self.setDragMode(QGraphicsView.DragMode.NoDrag) self.setCursor(QCursor(Qt.CursorShape.CrossCursor)) self.horizontal_line = QGraphicsLineItem() + self.horizontal_line.setZValue(5) self.vertical_line = QGraphicsLineItem() + self.vertical_line.setZValue(5) self.scene().addItem(self.horizontal_line) self.scene().addItem(self.vertical_line) self.update_lines_pos() @@ -558,7 +489,7 @@ def update_lines_pos(self): self.vertical_line.setLine(self.last_pos.x(), view_rect.top(), self.last_pos.x(), view_rect.bottom()) - def mousePressEvent(self, event): + def mousePressEvent(self, event: QMouseEvent): if self.insertion_mode and event.button() == Qt.MouseButton.LeftButton: rect_type = self.image_viewer.marking_to_add if rect_type == ImageMarking.NONE: @@ -568,6 +499,9 @@ def mousePressEvent(self, event): else: rect_type = ImageMarking.HINT + self.image_viewer.proxy_image_index.model().sourceModel().add_to_undo_stack( + action_name=f'Add {rect_type.value}', should_ask_for_confirmation=False) + self.image_viewer.add_rectangle(QRect(self.last_pos, QSize(0, 0)), rect_type) self.set_insertion_mode(False) @@ -575,14 +509,13 @@ def mousePressEvent(self, event): else: super().mousePressEvent(event) - def mouseMoveEvent(self, event): + def mouseMoveEvent(self, event: QMouseEvent): last_pos_raw = self.mapToScene(event.position().toPoint()) if ((event.modifiers() & Qt.KeyboardModifier.ShiftModifier) == Qt.KeyboardModifier.ShiftModifier): self.last_pos = grid.snap(last_pos_raw.toPoint()).toPoint() else: self.last_pos = last_pos_raw.toPoint() - pos = last_pos_raw if self.insertion_mode: self.update_lines_pos() @@ -613,6 +546,7 @@ class ImageViewer(QWidget): def __init__(self, proxy_image_list_model: ProxyImageListModel): super().__init__() + self.inhibit_reload_image = False self.proxy_image_list_model = proxy_image_list_model MarkingItem.pen_half_width = round(self.devicePixelRatio()) MarkingItem.zoom_factor = 1.0 @@ -623,6 +557,7 @@ def __init__(self, proxy_image_list_model: ProxyImageListModel): self.marking_to_add = ImageMarking.NONE self.scene = QGraphicsScene() self.view = ImageGraphicsView(self.scene, self) + self.view.setOptimizationFlags(QGraphicsView.DontSavePainterState) self.crop_marking: ImageMarking | None = None settings.change.connect(self.setting_change) @@ -636,30 +571,38 @@ def __init__(self, proxy_image_list_model: ProxyImageListModel): self.view.wheelEvent = self.wheelEvent @Slot() - def load_image(self, proxy_image_index: QModelIndex): - self.proxy_image_index = QPersistentModelIndex(proxy_image_index) - - self.marking_items.clear() - self.view.clear_scene() - if not self.proxy_image_index.isValid(): + def load_image(self, proxy_image_index: QModelIndex, is_complete = True): + persistent_image_index = QPersistentModelIndex(proxy_image_index) + if ((not persistent_image_index.isValid()) or + (self.inhibit_reload_image and + persistent_image_index == self.proxy_image_index)): return + self.proxy_image_index = persistent_image_index image: Image = self.proxy_image_index.data(Qt.ItemDataRole.UserRole) - pixmap = QPixmap(str(image.path)) - image_item = QGraphicsPixmapItem(pixmap) - self.scene.addItem(image_item) - self.scene.setSceneRect(image_item.boundingRect() - .adjusted(-1, -1, 1, 1)) # space for rect border - MarkingItem.image_size = image_item.boundingRect().toRect() - self.zoom_fit() - self.hud_item = ResizeHintHUD(MarkingItem.image_size) - self.scene.addItem(self.hud_item) + if is_complete: + self.marking_items.clear() + self.view.clear_scene() + pixmap = QPixmap(str(image.path)) + image_item = QGraphicsPixmapItem(pixmap) + image_item.setZValue(0) + self.scene.setSceneRect(image_item.boundingRect() + .adjusted(-1, -1, 1, 1)) # space for rect border + self.scene.addItem(image_item) + MarkingItem.image_size = image_item.boundingRect().toRect() + self.zoom_fit() + + self.hud_item = ResizeHintHUD(MarkingItem.image_size, image_item) + else: + for item in self.marking_items: + self.scene.removeItem(item) + self.marking_items.clear() self.marking_to_add = ImageMarking.NONE self.marking.emit(ImageMarking.NONE) - self.accept_crop_addition.emit(image.crop == None) - if image.crop != None: + self.accept_crop_addition.emit(image.crop is None) + if image.crop is not None: self.add_rectangle(image.crop, ImageMarking.CROP) else: calculate_grid(MarkingItem.image_size) @@ -734,7 +677,9 @@ def add_marking(self, marking: ImageMarking): @Slot() def change_marking(self, items: list[MarkingItem] | None = None, new_marking: ImageMarking = ImageMarking.NONE): - if items == None: + self.proxy_image_index.model().sourceModel().add_to_undo_stack( + action_name=f'Change marking', should_ask_for_confirmation=False) + if items is None: items = self.scene.selectedItems() for item in items: if new_marking == ImageMarking.NONE: @@ -789,7 +734,6 @@ def add_rectangle(self, rect: QRect, rect_type: ImageMarking, marking_item = MarkingItem(rect, rect_type, size) marking_item.setVisible(self.show_marking_state) if rect_type == ImageMarking.CROP: - marking_item.signal.move.connect(self.hud_item.setValues) self.crop_marking = marking_item marking_item.size_changed() # call after self.crop_marking was set! elif name == '' and rect_type != ImageMarking.NONE: @@ -801,14 +745,15 @@ def add_rectangle(self, rect: QRect, rect_type: ImageMarking, marking_item.setData(0, name) if rect_type != ImageMarking.CROP and rect_type != ImageMarking.NONE: label_background = QGraphicsRectItem(marking_item) + label_background.setZValue(2) label_background.setBrush(marking_item.color) label_background.setPen(Qt.NoPen) label_background.setVisible(self.show_label_state) marking_item.label = MarkingLabel(name, label_background) + marking_item.label.setZValue(2) marking_item.label.setVisible(self.show_label_state) marking_item.label.editingFinished.connect(self.label_changed) marking_item.adjust_layout() - marking_item.signal.change.connect(self.marking_changed) self.scene.addItem(marking_item) self.marking_items.append(marking_item) self.marking.emit(ImageMarking.NONE) @@ -819,6 +764,8 @@ def add_rectangle(self, rect: QRect, rect_type: ImageMarking, def label_changed(self, do_emit = True): """Slot to call when a marking label was changed to sync the information in the image.""" + self.proxy_image_index.model().sourceModel().add_to_undo_stack( + action_name=f'Change label', should_ask_for_confirmation=False) image: Image = self.proxy_image_index.data(Qt.ItemDataRole.UserRole) image.markings.clear() for marking in self.marking_items: @@ -827,9 +774,6 @@ def label_changed(self, do_emit = True): image.markings.append(Marking(marking.data(0), marking.rect_type, marking.rect().toRect())) - if do_emit: - self.proxy_image_list_model.sourceModel().dataChanged.emit( - self.proxy_image_index, self.proxy_image_index) @Slot(QGraphicsRectItem) def marking_changed(self, marking: QGraphicsRectItem): @@ -844,12 +788,16 @@ def marking_changed(self, marking: QGraphicsRectItem): image.crop = marking.rect().toRect() # ensure int! image.target_dimension = grid.target else: - image.markings = [Marking(marking.data(0), - marking.rect_type, - marking.rect().toRect()) - for marking in self.marking_items if marking.rect_type != ImageMarking.CROP] - self.proxy_image_list_model.sourceModel().dataChanged.emit( - self.proxy_image_index, self.proxy_image_index) + image.markings = [Marking(m.data(0), + m.rect_type, + m.rect().toRect()) + for m in self.marking_items if m.rect_type != ImageMarking.CROP] + if marking.rect_type == ImageMarking.CROP: + self.inhibit_reload_image = True + self.proxy_image_list_model.sourceModel().dataChanged.emit( + self.proxy_image_index, self.proxy_image_index, + [Qt.ItemDataRole.DecorationRole, Qt.ItemDataRole.SizeHintRole, Qt.ToolTipRole]) + self.inhibit_reload_image = False def get_selected_type(self) -> ImageMarking: if len(self.scene.selectedItems()) > 0: @@ -860,9 +808,12 @@ def get_selected_type(self) -> ImageMarking: def delete_markings(self, items: list[MarkingItem] | None = None): """Slot to delete the list of items or when items = None all currently selected marking items.""" + self.proxy_image_index.model().sourceModel().add_to_undo_stack( + action_name=f'Delete marking', should_ask_for_confirmation=False) image: Image = self.proxy_image_index.data(Qt.ItemDataRole.UserRole) - if items == None: + if items is None: items = self.scene.selectedItems() + tmp_delete_crop = False for item in items: if item.rect_type == ImageMarking.CROP: self.crop_marking = None @@ -871,12 +822,15 @@ def delete_markings(self, items: list[MarkingItem] | None = None): image.target_dimension = None self.accept_crop_addition.emit(True) calculate_grid(MarkingItem.image_size) + tmp_delete_crop = True else: self.marking_items.remove(item) self.label_changed(False) self.scene.removeItem(item) - self.proxy_image_list_model.sourceModel().dataChanged.emit( - self.proxy_image_index, self.proxy_image_index) + if tmp_delete_crop: + self.proxy_image_list_model.sourceModel().dataChanged.emit( + self.proxy_image_index, self.proxy_image_index, + [Qt.ItemDataRole.DecorationRole, Qt.ItemDataRole.SizeHintRole, Qt.ToolTipRole]) def resizeEvent(self, event): super().resizeEvent(event) diff --git a/taggui/widgets/main_window.py b/taggui/widgets/main_window.py index 0c29cac6..3dbeff22 100644 --- a/taggui/widgets/main_window.py +++ b/taggui/widgets/main_window.py @@ -593,6 +593,12 @@ def connect_image_list_signals(self): self.image_list_model.images)) self.image_list_model.dataChanged.connect( self.image_tags_editor.reload_image_tags_if_changed) + self.image_list_model.dataChanged.connect( + lambda start, end, roles: + self.image_viewer.load_image(self.image_viewer.proxy_image_index, + False) + if (start.row() <= self.image_viewer.proxy_image_index.row() and + self.image_viewer.proxy_image_index.row() <= end.row()) else 0) self.image_list_model.update_undo_and_redo_actions_requested.connect( self.update_undo_and_redo_actions) # Rows are inserted or removed from the proxy image list model when the From 5f6a2505fcd33777fed4429e1cc5cb54a1d42ced Mon Sep 17 00:00:00 2001 From: StableLlama Date: Mon, 17 Mar 2025 22:05:16 +0100 Subject: [PATCH 38/82] Fix zoom to fit --- taggui/models/image_list_model.py | 6 +- taggui/widgets/image_viewer.py | 96 ++++++++++++++++++------------- taggui/widgets/main_window.py | 2 +- 3 files changed, 58 insertions(+), 46 deletions(-) diff --git a/taggui/models/image_list_model.py b/taggui/models/image_list_model.py index b87e39ce..831b98d3 100644 --- a/taggui/models/image_list_model.py +++ b/taggui/models/image_list_model.py @@ -61,11 +61,6 @@ def __init__(self, image_list_image_width: int, tag_separator: str): self.redo_stack = [] self.proxy_image_list_model = None self.image_list_selection_model = None - self.dataChanged.connect(lambda start, end: [ - self.write_meta_to_disk( - start.sibling(row, start.column()) - .data(Qt.ItemDataRole.UserRole)) - for row in range(start.row(), end.row()+1)]) def rowCount(self, parent=None) -> int: return len(self.images) @@ -306,6 +301,7 @@ def restore_history_tags(self, is_undo: bool): image.crop = history_image_tags['crop'] image.markings = history_image_tags['markings'] self.write_image_tags_to_disk(image) + self.write_meta_to_disk(image) if changed_image_indices: self.dataChanged.emit(self.index(changed_image_indices[0]), self.index(changed_image_indices[-1])) diff --git a/taggui/widgets/image_viewer.py b/taggui/widgets/image_viewer.py index 6d5fd847..58fc26af 100644 --- a/taggui/widgets/image_viewer.py +++ b/taggui/widgets/image_viewer.py @@ -57,7 +57,6 @@ def __init__(self, rect: QRect, rect_type: ImageMarking, super().__init__(rect.toRectF(), parent) self.setFlag(QGraphicsItem.ItemIsSelectable, True) self.setFlag(QGraphicsItem.ItemSendsScenePositionChanges, True) - self.setAcceptHoverEvents(True) self.rect_type = rect_type self.label: MarkingLabel | None = None self.color = marking_colors[rect_type] @@ -67,6 +66,7 @@ def __init__(self, rect: QRect, rect_type: ImageMarking, self.setZValue(2) if rect_type in [ImageMarking.INCLUDE, ImageMarking.EXCLUDE]: self.area = QGraphicsRectItem(self) + self.area.setVisible(self.show_marking_latent) self.area.setFlag(QGraphicsItem.ItemStacksBehindParent) self.area.setZValue(1) area_color = QColor(self.color) @@ -94,21 +94,6 @@ def handleAt(self, point: QPointF) -> RectPosition: point.y() < self.rect().top() + handle_space, point.y() > self.rect().bottom() - handle_space) - def hoverMoveEvent(self, event): - handle = self.handleAt(event.pos()) - if handle == RectPosition.TL or handle == RectPosition.BR: - self.setCursor(Qt.SizeFDiagCursor) - elif handle == RectPosition.TR or handle == RectPosition.BL: - self.setCursor(Qt.SizeBDiagCursor) - elif handle == RectPosition.TOP or handle == RectPosition.BOTTOM: - self.setCursor(Qt.SizeVerCursor) - elif handle == RectPosition.LEFT or handle == RectPosition.RIGHT: - self.setCursor(Qt.SizeHorCursor) - else: - self.unsetCursor() - event.ignore() - super().hoverMoveEvent(event) - def mousePressEvent(self, event): self.handle_selected = self.handleAt(event.pos()) if (event.button() == Qt.MouseButton.LeftButton and @@ -116,7 +101,6 @@ def mousePressEvent(self, event): self.image_view.image_viewer.proxy_image_index.model().sourceModel().add_to_undo_stack( action_name=f'Change marking geometry', should_ask_for_confirmation=False) self.mouse_press_pos = event.pos() - self.mouse_press_scene_pos = event.scenePos() self.mouse_press_rect = self.rect() self.setZValue(4) self.move(False) @@ -409,6 +393,7 @@ def __init__(self, scene, image_viewer): self.setResizeAnchor(QGraphicsView.ViewportAnchor.AnchorUnderMouse) self.image_viewer = image_viewer MarkingItem.image_view = self + self.setMouseTracking(True) self.last_pos = None self.clear_scene() @@ -459,7 +444,6 @@ def set_insertion_mode(self, mode): self.insertion_mode = mode if mode: self.setDragMode(QGraphicsView.DragMode.NoDrag) - self.setCursor(QCursor(Qt.CursorShape.CrossCursor)) self.horizontal_line = QGraphicsLineItem() self.horizontal_line.setZValue(5) self.vertical_line = QGraphicsLineItem() @@ -469,7 +453,6 @@ def set_insertion_mode(self, mode): self.update_lines_pos() else: self.setDragMode(QGraphicsView.DragMode.ScrollHandDrag) - self.unsetCursor() if self.horizontal_line: self.scene().removeItem(self.horizontal_line) self.horizontal_line = None @@ -510,12 +493,38 @@ def mousePressEvent(self, event: QMouseEvent): super().mousePressEvent(event) def mouseMoveEvent(self, event: QMouseEvent): - last_pos_raw = self.mapToScene(event.position().toPoint()) + scene_pos = self.mapToScene(event.position().toPoint()) + items = self.scene().items(scene_pos) + cursor = None + + if self.insertion_mode: + cursor = Qt.CursorShape.CrossCursor + else: + for item in items: + if isinstance(item, MarkingItem): + handle = item.handleAt(scene_pos) + if handle == RectPosition.NONE: + continue + elif handle == RectPosition.TL or handle == RectPosition.BR: + cursor = Qt.CursorShape.SizeFDiagCursor + elif handle == RectPosition.TR or handle == RectPosition.BL: + cursor = Qt.CursorShape.SizeBDiagCursor + elif handle == RectPosition.TOP or handle == RectPosition.BOTTOM: + cursor = Qt.CursorShape.SizeVerCursor + elif handle == RectPosition.LEFT or handle == RectPosition.RIGHT: + cursor = Qt.CursorShape.SizeHorCursor + break + if cursor is None: + self.setDragMode(QGraphicsView.DragMode.ScrollHandDrag) + else: + self.setDragMode(QGraphicsView.DragMode.NoDrag) + self.setCursor(cursor) + if ((event.modifiers() & Qt.KeyboardModifier.ShiftModifier) == Qt.KeyboardModifier.ShiftModifier): - self.last_pos = grid.snap(last_pos_raw.toPoint()).toPoint() + self.last_pos = grid.snap(scene_pos.toPoint()).toPoint() else: - self.last_pos = last_pos_raw.toPoint() + self.last_pos = scene_pos.toPoint() if self.insertion_mode: self.update_lines_pos() @@ -538,6 +547,11 @@ def keyReleaseEvent(self, event): self.set_insertion_mode(False) super().keyReleaseEvent(event) + def resizeEvent(self, event): + super().resizeEvent(event) + if self.image_viewer.is_zoom_to_fit: + self.image_viewer.zoom_fit() + class ImageViewer(QWidget): zoom = Signal(float, name='zoomChanged') @@ -636,10 +650,9 @@ def zoom_in(self, center_pos: QPoint = None): def zoom_out(self, center_pos: QPoint = None): view = self.view.viewport().size() scene = self.scene.sceneRect() - MarkingItem.zoom_factor = max(MarkingItem.zoom_factor / 1.25, - min(view.width()/scene.width(), - view.height()/scene.height())) - self.is_zoom_to_fit = False + limit = min(view.width()/scene.width(), view.height()/scene.height()) + MarkingItem.zoom_factor = max(MarkingItem.zoom_factor / 1.25, limit) + self.is_zoom_to_fit = MarkingItem.zoom_factor == limit self.zoom_emit() @Slot() @@ -714,15 +727,17 @@ def show_marking_latent(self, checked: bool): MarkingItem.show_marking_latent = checked for marking in self.marking_items: if marking.rect_type in [ImageMarking.INCLUDE, ImageMarking.EXCLUDE]: - marking.update() + marking.area.setVisible(checked) def wheelEvent(self, event): old_pos = self.view.mapToScene(event.position().toPoint()) if event.angleDelta().y() > 0: self.zoom_in() - else: + elif event.angleDelta().y() < 0: self.zoom_out() + else: + return new_pos = self.view.mapToScene(event.position().toPoint()) delta = new_pos - old_pos @@ -774,6 +789,7 @@ def label_changed(self, do_emit = True): image.markings.append(Marking(marking.data(0), marking.rect_type, marking.rect().toRect())) + self.proxy_image_list_model.sourceModel().write_meta_to_disk(image) @Slot(QGraphicsRectItem) def marking_changed(self, marking: QGraphicsRectItem): @@ -787,17 +803,18 @@ def marking_changed(self, marking: QGraphicsRectItem): image.thumbnail = None image.crop = marking.rect().toRect() # ensure int! image.target_dimension = grid.target + self.inhibit_reload_image = True + self.proxy_image_list_model.sourceModel().dataChanged.emit( + self.proxy_image_index, self.proxy_image_index, + [Qt.ItemDataRole.DecorationRole, Qt.ItemDataRole.SizeHintRole, + Qt.ToolTipRole, Qt.ItemDataRole.UserRole]) + self.inhibit_reload_image = False else: image.markings = [Marking(m.data(0), m.rect_type, m.rect().toRect()) for m in self.marking_items if m.rect_type != ImageMarking.CROP] - if marking.rect_type == ImageMarking.CROP: - self.inhibit_reload_image = True - self.proxy_image_list_model.sourceModel().dataChanged.emit( - self.proxy_image_index, self.proxy_image_index, - [Qt.ItemDataRole.DecorationRole, Qt.ItemDataRole.SizeHintRole, Qt.ToolTipRole]) - self.inhibit_reload_image = False + self.proxy_image_list_model.sourceModel().write_meta_to_disk(image) def get_selected_type(self) -> ImageMarking: if len(self.scene.selectedItems()) > 0: @@ -813,7 +830,6 @@ def delete_markings(self, items: list[MarkingItem] | None = None): image: Image = self.proxy_image_index.data(Qt.ItemDataRole.UserRole) if items is None: items = self.scene.selectedItems() - tmp_delete_crop = False for item in items: if item.rect_type == ImageMarking.CROP: self.crop_marking = None @@ -822,15 +838,15 @@ def delete_markings(self, items: list[MarkingItem] | None = None): image.target_dimension = None self.accept_crop_addition.emit(True) calculate_grid(MarkingItem.image_size) - tmp_delete_crop = True + self.proxy_image_list_model.sourceModel().dataChanged.emit( + self.proxy_image_index, self.proxy_image_index, + [Qt.ItemDataRole.DecorationRole, Qt.ItemDataRole.SizeHintRole, + Qt.ToolTipRole, Qt.ItemDataRole.UserRole]) else: self.marking_items.remove(item) self.label_changed(False) + self.proxy_image_list_model.sourceModel().write_meta_to_disk(image) self.scene.removeItem(item) - if tmp_delete_crop: - self.proxy_image_list_model.sourceModel().dataChanged.emit( - self.proxy_image_index, self.proxy_image_index, - [Qt.ItemDataRole.DecorationRole, Qt.ItemDataRole.SizeHintRole, Qt.ToolTipRole]) def resizeEvent(self, event): super().resizeEvent(event) diff --git a/taggui/widgets/main_window.py b/taggui/widgets/main_window.py index 3dbeff22..891d5c09 100644 --- a/taggui/widgets/main_window.py +++ b/taggui/widgets/main_window.py @@ -582,7 +582,7 @@ def connect_image_list_signals(self): self.image_list_selection_model.currentChanged.connect( self.image_list.update_image_index_label) self.image_list_selection_model.currentChanged.connect( - self.image_viewer.load_image) + lambda current, previous: self.image_viewer.load_image(current)) self.image_list_selection_model.currentChanged.connect( self.image_tags_editor.load_image_tags) self.image_list_model.modelReset.connect( From 96602b0e8cc4efa20d4acad772de6da4268e88a3 Mon Sep 17 00:00:00 2001 From: StableLlama Date: Mon, 17 Mar 2025 23:17:39 +0100 Subject: [PATCH 39/82] Fix snap to grid for INCLUDE and little bugfix --- taggui/widgets/image_viewer.py | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/taggui/widgets/image_viewer.py b/taggui/widgets/image_viewer.py index 58fc26af..eba6271b 100644 --- a/taggui/widgets/image_viewer.py +++ b/taggui/widgets/image_viewer.py @@ -13,9 +13,8 @@ from utils.image import Image, ImageMarking, Marking import utils.target_dimension as target_dimension from utils.grid import Grid -from utils.rect import (RectPosition, flip_rect_position, change_rect, - change_rect_to_match_size) - +from utils.rect import (change_rect, change_rect_to_match_size, + flip_rect_position, get_rect_position, RectPosition) # Grid for alignment to latent space grid = Grid(QRect(0, 0, 1, 1)) @@ -141,10 +140,21 @@ def mouseMoveEvent(self, event): rect = change_rect(self.rect(), self.handle_selected, event.pos()) - rect = QRectF(grid.snap(rect.toRect().topLeft(), floor), - grid.snap(rect.toRect().bottomRight(), ceil)) - rect = QRect(QPoint(ceil(rect.topLeft().x()),ceil(rect.topLeft().y())), - QPoint(floor(rect.bottomRight().x()),floor(rect.bottomRight().y()))) + + round_tl = round + round_br = round + if self.rect_type == ImageMarking.EXCLUDE: + round_tl = floor + round_br = ceil + elif self.rect_type == ImageMarking.INCLUDE: + round_tl = ceil + round_br = floor + rect = QRectF(grid.snap(rect.toRect().topLeft(), round_tl), + grid.snap(rect.toRect().bottomRight(), round_br)) + rect = QRect(QPoint(round_br(rect.topLeft().x()), + round_br(rect.topLeft().y())), + QPoint(round_tl(rect.bottomRight().x()), + round_tl(rect.bottomRight().y()))) else: pos_quantized = event.pos().toPoint() rect = change_rect(self.rect().toRect(), From 30d9fa8905a8249476056dce6821be56a60805f6 Mon Sep 17 00:00:00 2001 From: StableLlama Date: Mon, 17 Mar 2025 23:42:37 +0100 Subject: [PATCH 40/82] Insertion mode even when not focused on the image view --- taggui/widgets/image_viewer.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/taggui/widgets/image_viewer.py b/taggui/widgets/image_viewer.py index eba6271b..70eb1e90 100644 --- a/taggui/widgets/image_viewer.py +++ b/taggui/widgets/image_viewer.py @@ -181,6 +181,9 @@ def mouseReleaseEvent(self, event): self.move(False) self.image_view.image_viewer.marking_changed(self) self.setZValue(2) + if ((event.modifiers() & Qt.KeyboardModifier.ControlModifier) == + Qt.KeyboardModifier.ControlModifier): + self.image_view.set_insertion_mode(True) super().mouseReleaseEvent(event) def paint(self, painter, option, widget=None): @@ -506,6 +509,12 @@ def mouseMoveEvent(self, event: QMouseEvent): scene_pos = self.mapToScene(event.position().toPoint()) items = self.scene().items(scene_pos) cursor = None + if ((event.modifiers() & Qt.KeyboardModifier.ControlModifier) == + Qt.KeyboardModifier.ControlModifier and not self.insertion_mode): + self.set_insertion_mode(True) + elif ((event.modifiers() & Qt.KeyboardModifier.ControlModifier) != + Qt.KeyboardModifier.ControlModifier and self.insertion_mode): + self.set_insertion_mode(False) if self.insertion_mode: cursor = Qt.CursorShape.CrossCursor @@ -857,6 +866,3 @@ def delete_markings(self, items: list[MarkingItem] | None = None): self.label_changed(False) self.proxy_image_list_model.sourceModel().write_meta_to_disk(image) self.scene.removeItem(item) - - def resizeEvent(self, event): - super().resizeEvent(event) From 1e95af016e67f0aee1705027488d497975dbead2 Mon Sep 17 00:00:00 2001 From: StableLlama Date: Wed, 19 Mar 2025 22:14:58 +0100 Subject: [PATCH 41/82] Show guiding lines for crop. Also simplify code for hyperbola --- taggui/widgets/image_viewer.py | 66 +++++++++++++++++++++++----------- 1 file changed, 45 insertions(+), 21 deletions(-) diff --git a/taggui/widgets/image_viewer.py b/taggui/widgets/image_viewer.py index 70eb1e90..8298f4a9 100644 --- a/taggui/widgets/image_viewer.py +++ b/taggui/widgets/image_viewer.py @@ -1,4 +1,4 @@ -from math import ceil, floor +from math import ceil, floor, sqrt from PySide6.QtCore import (QModelIndex, QPersistentModelIndex, QPoint, QPointF, QRect, QRectF, QSize, Qt, Signal, Slot) from PySide6.QtGui import (QAction, QActionGroup, QCursor, QColor, QIcon, @@ -16,6 +16,9 @@ from utils.rect import (change_rect, change_rect_to_match_size, flip_rect_position, get_rect_position, RectPosition) +# The (inverse) golden ratio for showing hints during cropping +golden_ratio = 2 / (1 + sqrt(5)) + # Grid for alignment to latent space grid = Grid(QRect(0, 0, 1, 1)) @@ -50,6 +53,10 @@ class MarkingItem(QGraphicsRectItem): # Static link to the single ImageGraphicsView in this application image_view = None show_marking_latent = True + handle_selected = None + mouse_press_pos = None + mouse_press_rect = None + show_crop_hint = True def __init__(self, rect: QRect, rect_type: ImageMarking, parent = None): @@ -59,9 +66,6 @@ def __init__(self, rect: QRect, rect_type: ImageMarking, self.rect_type = rect_type self.label: MarkingLabel | None = None self.color = marking_colors[rect_type] - self.handle_selected = None - self.mouse_press_pos = None - self.mouse_press_rect = None self.setZValue(2) if rect_type in [ImageMarking.INCLUDE, ImageMarking.EXCLUDE]: self.area = QGraphicsRectItem(self) @@ -94,6 +98,7 @@ def handleAt(self, point: QPointF) -> RectPosition: point.y() > self.rect().bottom() - handle_space) def mousePressEvent(self, event): + self.show_crop_hint = (event.modifiers() & Qt.KeyboardModifier.AltModifier) != Qt.KeyboardModifier.AltModifier self.handle_selected = self.handleAt(event.pos()) if (event.button() == Qt.MouseButton.LeftButton and self.handle_selected != RectPosition.NONE): @@ -108,6 +113,7 @@ def mousePressEvent(self, event): def mouseMoveEvent(self, event): if self.handle_selected: + self.show_crop_hint = (event.modifiers() & Qt.KeyboardModifier.AltModifier) != Qt.KeyboardModifier.AltModifier if ((event.modifiers() & Qt.KeyboardModifier.ShiftModifier) == Qt.KeyboardModifier.ShiftModifier): if self.rect_type == ImageMarking.CROP: @@ -188,6 +194,29 @@ def mouseReleaseEvent(self, event): def paint(self, painter, option, widget=None): if self.rect_type == ImageMarking.CROP: + if (self.show_crop_hint and self.handle_selected and + self.handle_selected != RectPosition.NONE): + hint_line_crossings = [ + self.rect().center(), + self.rect().topLeft() + QPointF(self.rect().width()*golden_ratio, + self.rect().height()*golden_ratio), + self.rect().bottomRight() - QPointF(self.rect().width()*golden_ratio, + self.rect().height()*golden_ratio), + self.rect().topLeft() + QPointF(self.rect().width()/3, + self.rect().height()/3), + self.rect().bottomRight() - QPointF(self.rect().width()/3, + self.rect().height()/3)] + lint_line_style = [Qt.SolidLine, Qt.DotLine, Qt.DotLine, Qt.DashLine, Qt.DashLine] + for crossing, style in zip(hint_line_crossings, lint_line_style): + path = QPainterPath() + path.moveTo(self.rect().x(), crossing.y()) + path.lineTo(self.rect().right(), crossing.y()) + path.moveTo(crossing.x(), self.rect().y()) + path.lineTo(crossing.x(), self.rect().bottom()) + painter.setPen(QPen(QColor(255, 255, 255, 127), 3 / self.zoom_factor)) + painter.drawPath(path) + painter.setPen(QPen(QColor(0, 0, 0), 1 / self.zoom_factor, style)) + painter.drawPath(path) painter.setPen(Qt.NoPen) painter.setBrush(QColor(255, 0, 0, 127)) path = QPainterPath() @@ -356,23 +385,18 @@ def add_line_limit_lr(self, y: float, td: int): def add_hyperbola_limit(self, pos: QPointF, lr: int, td: int): target_area = settings.value('export_resolution', type=int)**2 res_size = max(settings.value('export_bucket_res_size', type=int), 1) - dx = res_size - first = QPointF(pos.x() + lr * dx, pos.y() + td * target_area / dx) - while dx * res_size <= target_area: - p = QPointF(pos.x() + lr * dx, pos.y() + td * target_area / dx) - dx = dx + 50 - # add all points above the parent as well as one before and one after - if self.parentItem().contains(p): - if first: - self.path.moveTo(first) - first = None - self.path.lineTo(p) - else: - if first: - first = p - else: - self.path.lineTo(p) - break + if td < 0: + distance_x = target_area / (pos.y() - self._boundingRect.y()) + else: + distance_x = target_area / (self._boundingRect.bottom() - pos.y()) + x = self._boundingRect.x() if lr < 0 else pos.x() + distance_x + end_x = pos.x() - distance_x if lr < 0 else self._boundingRect.right() + first = True + while x < end_x + 50: + p = QPointF(x, pos.y() + td * target_area / (lr * (x - pos.x()))) + self.path.moveTo(p) if first else self.path.lineTo(p) + first = False + x += 50 for ar in target_dimension.get_preferred_sizes(): s = max(res_size / ar[0], res_size / ar[1]) From e5b951f7d6f8620bb16b5d3e9f4f10345aac54e1 Mon Sep 17 00:00:00 2001 From: StableLlama Date: Fri, 21 Mar 2025 01:05:44 +0100 Subject: [PATCH 42/82] Fix adding of markings. --- taggui/utils/rect.py | 13 ++- taggui/widgets/image_viewer.py | 160 +++++++++++++++++---------------- 2 files changed, 96 insertions(+), 77 deletions(-) diff --git a/taggui/utils/rect.py b/taggui/utils/rect.py index 4d01a402..341a8d52 100644 --- a/taggui/utils/rect.py +++ b/taggui/utils/rect.py @@ -1,7 +1,7 @@ from enum import Enum from math import floor, ceil -from PySide6.QtCore import QPoint, QRect, QSize, QPointF, QRectF +from PySide6.QtCore import QPoint, QRect, QSize, QPointF, QRectF, Qt class RectPosition(str, Enum): @@ -15,6 +15,17 @@ class RectPosition(str, Enum): LEFT = 'left' NONE = 'none' +def map_rect_position_to_cursor(handle: RectPosition) -> Qt.CursorShape | None: + if handle == RectPosition.TL or handle == RectPosition.BR: + return Qt.CursorShape.SizeFDiagCursor + elif handle == RectPosition.TR or handle == RectPosition.BL: + return Qt.CursorShape.SizeBDiagCursor + elif handle == RectPosition.TOP or handle == RectPosition.BOTTOM: + return Qt.CursorShape.SizeVerCursor + elif handle == RectPosition.LEFT or handle == RectPosition.RIGHT: + return Qt.CursorShape.SizeHorCursor + return None + def get_rect_position(left: bool, right: bool, top: bool, bottom: bool) -> RectPosition: if top: if left: diff --git a/taggui/widgets/image_viewer.py b/taggui/widgets/image_viewer.py index 8298f4a9..4078b01c 100644 --- a/taggui/widgets/image_viewer.py +++ b/taggui/widgets/image_viewer.py @@ -14,7 +14,8 @@ import utils.target_dimension as target_dimension from utils.grid import Grid from utils.rect import (change_rect, change_rect_to_match_size, - flip_rect_position, get_rect_position, RectPosition) + flip_rect_position, get_rect_position, + map_rect_position_to_cursor, RectPosition) # The (inverse) golden ratio for showing hints during cropping golden_ratio = 2 / (1 + sqrt(5)) @@ -44,25 +45,22 @@ def calculate_grid(content: QRect): class MarkingItem(QGraphicsRectItem): # the halved size of the pen in local coordinates to make sure it stays the # same during zooming - pen_half_width = 1.0 + pen_half_width: float = 1.0 # the minimal size of the active area in scene coordinates - handle_half_size = 5 - zoom_factor = 1.0 + handle_half_size: int = 5 + zoom_factor: float = 1.0 # The size of the image this rect belongs to - image_size = QRect(0, 0, 1, 1) + image_size: QRect = QRect(0, 0, 1, 1) # Static link to the single ImageGraphicsView in this application - image_view = None - show_marking_latent = True - handle_selected = None - mouse_press_pos = None - mouse_press_rect = None - show_crop_hint = True - - def __init__(self, rect: QRect, rect_type: ImageMarking, + image_view: bool = None + show_marking_latent: bool = True + handle_selected: RectPosition = RectPosition.NONE + show_crop_hint: bool = True + + def __init__(self, rect: QRect, rect_type: ImageMarking, interactive: bool, parent = None): super().__init__(rect.toRectF(), parent) self.setFlag(QGraphicsItem.ItemIsSelectable, True) - self.setFlag(QGraphicsItem.ItemSendsScenePositionChanges, True) self.rect_type = rect_type self.label: MarkingLabel | None = None self.color = marking_colors[rect_type] @@ -77,11 +75,13 @@ def __init__(self, rect: QRect, rect_type: ImageMarking, self.area.setBrush(area_color) self.area.setPen(Qt.NoPen) self.move(False) + if interactive: + MarkingItem.handle_selected = RectPosition.BR def move(self, is_in_move): if self.rect_type == ImageMarking.CROP: if not is_in_move: - self.image_view.image_viewer.hud_item.setValues(self.rect(), self.handle_selected) + self.image_view.image_viewer.hud_item.setValues(self.rect(), MarkingItem.handle_selected) elif self.rect_type == ImageMarking.INCLUDE: self.area.setRect(QRectF(grid.snap(self.rect().toRect().topLeft(), ceil), grid.snap(self.rect().toRect().adjusted(0,0,1,1).bottomRight(), floor))) @@ -98,28 +98,28 @@ def handleAt(self, point: QPointF) -> RectPosition: point.y() > self.rect().bottom() - handle_space) def mousePressEvent(self, event): - self.show_crop_hint = (event.modifiers() & Qt.KeyboardModifier.AltModifier) != Qt.KeyboardModifier.AltModifier - self.handle_selected = self.handleAt(event.pos()) + self.show_crop_hint = ((event.modifiers() & Qt.KeyboardModifier.AltModifier) != + Qt.KeyboardModifier.AltModifier) + MarkingItem.handle_selected = self.handleAt(event.pos()) if (event.button() == Qt.MouseButton.LeftButton and - self.handle_selected != RectPosition.NONE): + MarkingItem.handle_selected != RectPosition.NONE): self.image_view.image_viewer.proxy_image_index.model().sourceModel().add_to_undo_stack( action_name=f'Change marking geometry', should_ask_for_confirmation=False) - self.mouse_press_pos = event.pos() - self.mouse_press_rect = self.rect() self.setZValue(4) self.move(False) else: event.ignore() def mouseMoveEvent(self, event): - if self.handle_selected: - self.show_crop_hint = (event.modifiers() & Qt.KeyboardModifier.AltModifier) != Qt.KeyboardModifier.AltModifier + if MarkingItem.handle_selected != RectPosition.NONE: + self.show_crop_hint = ((event.modifiers() & Qt.KeyboardModifier.AltModifier) != + Qt.KeyboardModifier.AltModifier) if ((event.modifiers() & Qt.KeyboardModifier.ShiftModifier) == Qt.KeyboardModifier.ShiftModifier): if self.rect_type == ImageMarking.CROP: pos_quantized = event.pos().toPoint() rect_pre = change_rect(self.rect().toRect(), - self.handle_selected, + MarkingItem.handle_selected, pos_quantized) target = target_dimension.get(rect_pre.size()) if rect_pre.height() * target.width() / rect_pre.width() < target.height(): # too wide @@ -140,11 +140,11 @@ def mouseMoveEvent(self, event): else: target_size = target_size1 rect = change_rect_to_match_size(self.rect().toRect(), - self.handle_selected, + MarkingItem.handle_selected, target_size) else: rect = change_rect(self.rect(), - self.handle_selected, + MarkingItem.handle_selected, event.pos()) round_tl = round @@ -164,12 +164,12 @@ def mouseMoveEvent(self, event): else: pos_quantized = event.pos().toPoint() rect = change_rect(self.rect().toRect(), - self.handle_selected, + MarkingItem.handle_selected, pos_quantized) - self.handle_selected = flip_rect_position(self.handle_selected, - rect.width() < 0, - rect.height() < 0) + MarkingItem.handle_selected = flip_rect_position(self.handle_selected, + rect.width() < 0, + rect.height() < 0) if rect.width() == 0 or rect.height() == 0: self.setRect(rect) @@ -182,20 +182,19 @@ def mouseMoveEvent(self, event): super().mouseMoveEvent(event) def mouseReleaseEvent(self, event): - if self.handle_selected: - self.handle_selected = RectPosition.NONE + MarkingItem.handle_selected = RectPosition.NONE self.move(False) self.image_view.image_viewer.marking_changed(self) self.setZValue(2) if ((event.modifiers() & Qt.KeyboardModifier.ControlModifier) == Qt.KeyboardModifier.ControlModifier): - self.image_view.set_insertion_mode(True) + self.image_view.set_insertion_mode(self.rect_type) + self.ungrabMouse() super().mouseReleaseEvent(event) def paint(self, painter, option, widget=None): if self.rect_type == ImageMarking.CROP: - if (self.show_crop_hint and self.handle_selected and - self.handle_selected != RectPosition.NONE): + if self.show_crop_hint and MarkingItem.handle_selected != RectPosition.NONE: hint_line_crossings = [ self.rect().center(), self.rect().topLeft() + QPointF(self.rect().width()*golden_ratio, @@ -439,7 +438,7 @@ def showContextMenu(self, pos): item = self.scene().itemAt(scene_pos, self.transform()) if isinstance(item, MarkingLabel): item = item.parentItem().parentItem() - if isinstance(item, MarkingItem) and item.handle_selected != RectPosition.NONE: + if isinstance(item, MarkingItem) and MarkingItem.handle_selected != RectPosition.NONE: menu = QMenu() if item.rect_type != ImageMarking.NONE: if item.rect_type != ImageMarking.CROP: @@ -477,17 +476,20 @@ def clear_scene(self): self.vertical_line = None self.scene().clear() - def set_insertion_mode(self, mode): - self.insertion_mode = mode - if mode: - self.setDragMode(QGraphicsView.DragMode.NoDrag) - self.horizontal_line = QGraphicsLineItem() - self.horizontal_line.setZValue(5) - self.vertical_line = QGraphicsLineItem() - self.vertical_line.setZValue(5) - self.scene().addItem(self.horizontal_line) - self.scene().addItem(self.vertical_line) - self.update_lines_pos() + def set_insertion_mode(self, marking: ImageMarking): + old_insertion_mode = self.insertion_mode + self.insertion_mode = marking != ImageMarking.NONE + if self.insertion_mode: + if not old_insertion_mode: + self.setDragMode(QGraphicsView.DragMode.NoDrag) + self.horizontal_line = QGraphicsLineItem() + self.horizontal_line.setZValue(5) + self.vertical_line = QGraphicsLineItem() + self.vertical_line.setZValue(5) + self.scene().addItem(self.horizontal_line) + self.scene().addItem(self.vertical_line) + self.update_lines_pos() + self.image_viewer.marking.emit(marking) else: self.setDragMode(QGraphicsView.DragMode.ScrollHandDrag) if self.horizontal_line: @@ -495,6 +497,7 @@ def set_insertion_mode(self, mode): self.horizontal_line = None self.scene().removeItem(self.vertical_line) self.vertical_line = None + self.image_viewer.marking.emit(ImageMarking.NONE) def update_lines_pos(self): """Show the hint lines at the position self.last_pos. @@ -523,39 +526,28 @@ def mousePressEvent(self, event: QMouseEvent): action_name=f'Add {rect_type.value}', should_ask_for_confirmation=False) self.image_viewer.add_rectangle(QRect(self.last_pos, QSize(0, 0)), - rect_type) - self.set_insertion_mode(False) - super().mousePressEvent(event) - else: - super().mousePressEvent(event) + rect_type, interactive=True) + self.set_insertion_mode(ImageMarking.NONE) + self.setDragMode(QGraphicsView.DragMode.NoDrag) + return + super().mousePressEvent(event) def mouseMoveEvent(self, event: QMouseEvent): scene_pos = self.mapToScene(event.position().toPoint()) items = self.scene().items(scene_pos) cursor = None - if ((event.modifiers() & Qt.KeyboardModifier.ControlModifier) == - Qt.KeyboardModifier.ControlModifier and not self.insertion_mode): - self.set_insertion_mode(True) - elif ((event.modifiers() & Qt.KeyboardModifier.ControlModifier) != - Qt.KeyboardModifier.ControlModifier and self.insertion_mode): - self.set_insertion_mode(False) if self.insertion_mode: cursor = Qt.CursorShape.CrossCursor + elif MarkingItem.handle_selected != RectPosition.NONE: + cursor = map_rect_position_to_cursor(MarkingItem.handle_selected) else: for item in items: if isinstance(item, MarkingItem): handle = item.handleAt(scene_pos) if handle == RectPosition.NONE: continue - elif handle == RectPosition.TL or handle == RectPosition.BR: - cursor = Qt.CursorShape.SizeFDiagCursor - elif handle == RectPosition.TR or handle == RectPosition.BL: - cursor = Qt.CursorShape.SizeBDiagCursor - elif handle == RectPosition.TOP or handle == RectPosition.BOTTOM: - cursor = Qt.CursorShape.SizeVerCursor - elif handle == RectPosition.LEFT or handle == RectPosition.RIGHT: - cursor = Qt.CursorShape.SizeHorCursor + cursor = map_rect_position_to_cursor(handle) break if cursor is None: self.setDragMode(QGraphicsView.DragMode.ScrollHandDrag) @@ -575,19 +567,32 @@ def mouseMoveEvent(self, event: QMouseEvent): super().mouseMoveEvent(event) def keyPressEvent(self, event): - if event.key() == Qt.Key.Key_Control: - self.set_insertion_mode(True) - elif event.key() == Qt.Key.Key_Delete: + if event.key() == Qt.Key.Key_Delete: edited_item = self.scene().focusItem() if not (isinstance(edited_item, MarkingLabel) and edited_item.textInteractionFlags() == Qt.TextEditorInteraction): # Delete marking only when not editing the label self.image_viewer.delete_markings() + else: + if ((event.modifiers() & Qt.KeyboardModifier.ControlModifier) == + Qt.KeyboardModifier.ControlModifier): + if ((event.modifiers() & Qt.KeyboardModifier.AltModifier) == + Qt.KeyboardModifier.AltModifier): + self.set_insertion_mode(ImageMarking.EXCLUDE) + else: + self.set_insertion_mode(ImageMarking.HINT) super().keyPressEvent(event) def keyReleaseEvent(self, event): - if event.key() == Qt.Key.Key_Control: - self.set_insertion_mode(False) + if ((event.modifiers() & Qt.KeyboardModifier.ControlModifier) == + Qt.KeyboardModifier.ControlModifier): + if ((event.modifiers() & Qt.KeyboardModifier.AltModifier) == + Qt.KeyboardModifier.AltModifier): + self.set_insertion_mode(ImageMarking.EXCLUDE) + else: + self.set_insertion_mode(ImageMarking.HINT) + else: + self.set_insertion_mode(ImageMarking.NONE) super().keyReleaseEvent(event) def resizeEvent(self, event): @@ -660,11 +665,12 @@ def load_image(self, proxy_image_index: QModelIndex, is_complete = True): self.marking.emit(ImageMarking.NONE) self.accept_crop_addition.emit(image.crop is None) if image.crop is not None: - self.add_rectangle(image.crop, ImageMarking.CROP) + self.add_rectangle(image.crop, ImageMarking.CROP, interactive=False) else: calculate_grid(MarkingItem.image_size) for marking in image.markings: - self.add_rectangle(marking.rect, marking.type, name=marking.label) + self.add_rectangle(marking.rect, marking.type, interactive=False, + name=marking.label) @Slot() def setting_change(self, key, value): @@ -728,7 +734,7 @@ def zoom_emit(self): @Slot(ImageMarking) def add_marking(self, marking: ImageMarking): self.marking_to_add = marking - self.view.set_insertion_mode(marking != ImageMarking.NONE) + self.view.set_insertion_mode(marking) @Slot() def change_marking(self, items: list[MarkingItem] | None = None, @@ -787,9 +793,9 @@ def wheelEvent(self, event): self.view.translate(delta.x(), delta.y()) def add_rectangle(self, rect: QRect, rect_type: ImageMarking, - size: QSize = None, name: str = ''): + interactive: bool, size: QSize = None, name: str = ''): self.marking_to_add = ImageMarking.NONE - marking_item = MarkingItem(rect, rect_type, size) + marking_item = MarkingItem(rect, rect_type, interactive, size) marking_item.setVisible(self.show_marking_state) if rect_type == ImageMarking.CROP: self.crop_marking = marking_item @@ -814,7 +820,9 @@ def add_rectangle(self, rect: QRect, rect_type: ImageMarking, marking_item.adjust_layout() self.scene.addItem(marking_item) self.marking_items.append(marking_item) - self.marking.emit(ImageMarking.NONE) + if interactive: + self.scene.clearSelection() + marking_item.grabMouse() if rect_type == ImageMarking.CROP: self.accept_crop_addition.emit(False) From da0dd2bbf51a283d007fdcaf8f968e057c4a7d34 Mon Sep 17 00:00:00 2001 From: StableLlama Date: Sat, 22 Mar 2025 13:16:35 +0100 Subject: [PATCH 43/82] Show current crop information as image list overlay. Also bug fixing. --- taggui/dialogs/export_dialog.py | 20 ++---- taggui/models/image_list_model.py | 4 +- taggui/models/proxy_image_list_model.py | 4 +- taggui/utils/grid.py | 9 ++- taggui/utils/target_dimension.py | 45 +++++++++---- taggui/widgets/image_list.py | 57 ++++++++++++++++- taggui/widgets/image_viewer.py | 85 +++++++++++++++---------- taggui/widgets/main_window.py | 1 + 8 files changed, 154 insertions(+), 71 deletions(-) diff --git a/taggui/dialogs/export_dialog.py b/taggui/dialogs/export_dialog.py index 88322633..14a10a04 100644 --- a/taggui/dialogs/export_dialog.py +++ b/taggui/dialogs/export_dialog.py @@ -381,21 +381,12 @@ def show_statistics(self): resolution = settings.value('export_resolution', type=int) bucket_res = settings.value('export_bucket_res_size', type=int) - # notable aspect ratios - aspect_ratios = [ - (1, 1, 1), - (2, 1, 2/1), - (3, 2, 3/2), - (4, 3, 4/3), - (16, 9, 16/9), - (21, 9, 21/9), - ] - aspect_ratios = target_dimension.prepare(aspect_ratios) + aspect_ratios = target_dimension.prepare() image_list = self.get_image_list() image_dimensions = defaultdict(int) for this_image in image_list: - if this_image.crop != None: + if this_image.crop is not None: this_image.target_dimension = target_dimension.get( this_image.crop.size()) else: @@ -417,11 +408,8 @@ def show_statistics(self): aspect_ratio = width / height rowPosition = self.statistics_table.rowCount() notable_aspect_ratio = '' - for ar in aspect_ratios: - if abs(ar[2] - aspect_ratio) < 1e-3: - notable_aspect_ratio = f" ({ar[0]}:{ar[1]})" - elif abs(1/ar[2] - aspect_ratio) < 1e-3: - notable_aspect_ratio = f" ({ar[1]}:{ar[0]})" + ar = target_dimension.get_noteable_aspect_ratio(aspect_ratio) + notable_aspect_ratio = f' ({ar[0]}:{ar[1]})' if ar is not None else '' utilization = (width * height)**0.5 / resolution if resolution > 0 else 1 self.statistics_table.insertRow(rowPosition) diff --git a/taggui/models/image_list_model.py b/taggui/models/image_list_model.py index 831b98d3..54b397b9 100644 --- a/taggui/models/image_list_model.py +++ b/taggui/models/image_list_model.py @@ -102,14 +102,14 @@ def data(self, index, role=None) -> Image | str | QIcon | QSize: if role == Qt.ItemDataRole.SizeHintRole: if image.thumbnail: return image.thumbnail.availableSizes()[0] - dimensions = image.dimensions + dimensions = image.crop.size().toTuple() if image.crop else image.dimensions if not dimensions: return QSize(self.image_list_image_width, self.image_list_image_width) width, height = dimensions # Scale the dimensions to the image width. return QSize(self.image_list_image_width, - int(self.image_list_image_width * height / width)) + int(self.image_list_image_width * min(height / width, 3))) if role == Qt.ItemDataRole.ToolTipRole: path = image.path.relative_to(settings.value('directory_path', type=str)) dimensions = f'{image.dimensions[0]}:{image.dimensions[1]}' diff --git a/taggui/models/proxy_image_list_model.py b/taggui/models/proxy_image_list_model.py index 0961b424..6039c422 100644 --- a/taggui/models/proxy_image_list_model.py +++ b/taggui/models/proxy_image_list_model.py @@ -51,8 +51,8 @@ def does_image_match_filter(self, image: Image, if image.target_dimension == None: image.target_dimension = target_dimension.get(image.dimensions) return (len(dimension) == 2 - and dimension[0] == str(image.target_dimension[0]) - and dimension[1] == str(image.target_dimension[1])) + and dimension[0] == str(image.target_dimension.width()) + and dimension[1] == str(image.target_dimension.height())) if filter_[1] == 'AND': return (self.does_image_match_filter(image, filter_[0]) and self.does_image_match_filter(image, filter_[2:])) diff --git a/taggui/utils/grid.py b/taggui/utils/grid.py index 7273251b..b9ae2b01 100644 --- a/taggui/utils/grid.py +++ b/taggui/utils/grid.py @@ -20,6 +20,7 @@ def __init__(self, screen: QRect): self.target: QSize self.scale_x: float self.scale_y: float + self.aspect_ratio: tuple[int, int, float] | None self.update(screen) @@ -39,14 +40,15 @@ def update(self, screen: QRect | None = None): vis_size = self.screen.size() self.target = target_dimension.get(vis_size) + aspect_ratio = self.target.width() / self.target.height() if (bucket_strategy == BucketStrategy.CROP or bucket_strategy == BucketStrategy.CROP_SCALE): if (self.screen.height() * self.target.width() < self.target.height() * self.screen.width()): # too wide - vis_size.setWidth(floor(self.screen.height() * self.target.width() / self.target.height())) + vis_size.setWidth(floor(self.screen.height() * aspect_ratio)) else: - vis_size.setHeight(floor(self.screen.width() * self.target.height() / self.target.width())) + vis_size.setHeight(floor(self.screen.width() / aspect_ratio)) if bucket_strategy == BucketStrategy.CROP_SCALE: vis_size.setWidth(floor((self.screen.width() + vis_size.width())/2)) vis_size.setHeight(floor((self.screen.height() + vis_size.height())/2)) @@ -60,6 +62,9 @@ def update(self, screen: QRect | None = None): self.scale_x = self.target.width() / self.visible.width() self.scale_y = self.target.height() / self.visible.height() + self.aspect_ratio = target_dimension.get_noteable_aspect_ratio( + self.target.width(), self.target.height()) + def is_visible_equal_screen_size(self) -> bool: return self.screen.size() == self.visible diff --git a/taggui/utils/target_dimension.py b/taggui/utils/target_dimension.py index fe2487a1..b2c0f3ad 100644 --- a/taggui/utils/target_dimension.py +++ b/taggui/utils/target_dimension.py @@ -1,3 +1,4 @@ +import sys from math import floor, sqrt import re @@ -7,6 +8,15 @@ # singleton data store _preferred_sizes : list[QSize] = [] +notable_aspect_ratios = [ + (1, 1, 1), + (2, 1, 2/1), + (3, 2, 3/2), + (4, 3, 4/3), + (16, 9, 16/9), + (21, 9, 21/9), +] +aspect_ratios = notable_aspect_ratios settings.change.connect(lambda: _preferred_sizes.clear()) @@ -17,22 +27,19 @@ def get_preferred_sizes(): return _preferred_sizes -def prepare(aspect_ratios : list[tuple[int, int, float]] | None = None) -> list[tuple[int, int, float]] | None: +def prepare() -> list[tuple[int, int, float]] | None: """ Prepare by parsing the user supplied preferred sizes. - Parameters - ---------- - aspect_ratios : list(tuple[int, int, int]) | None - A list of typical aspect ratios to take care of - Return ------ The same list of aspect ratios (when supplied) but extrended by the real aspect ratios of the preferred sizes. """ global _preferred_sizes + global aspect_ratios _preferred_sizes = [] + aspect_ratios = notable_aspect_ratios for res_str in re.split(r'\s*,\s*', settings.value('export_preferred_sizes') or ''): try: if res_str == '': @@ -43,14 +50,18 @@ def prepare(aspect_ratios : list[tuple[int, int, float]] | None = None) -> list[ _preferred_sizes.append((width, height)) if not width == height: _preferred_sizes.append((height, width)) - if aspect_ratios != None: + if aspect_ratios is not None: # add exact aspect ratio of the preferred size to label it # similar to the perfect one aspect_ratio = width / height for ar in aspect_ratios: - if abs(ar[2] - aspect_ratio) < 0.15: + ar_delta = abs(ar[2] - aspect_ratio) + if ar_delta < 1e-4: + # already included + break + if ar_delta < 0.15: aspect_ratios.append((ar[0], ar[1], aspect_ratio)) - break + break except ValueError: # Handle cases where the resolution string is not in the correct format print(f'Warning: Invalid resolution format: {res_str}. Skipping.', @@ -88,8 +99,8 @@ def get(dimensions: QSize) -> QSize: return QSize((width // bucket_res)*bucket_res, (height // bucket_res)*bucket_res) if width < bucket_res or height < bucket_res: - # it doesn't make sense to use such a small image. But we shouldn't - # patronize the user + # It doesn't make sense to use such a small image. + # But we shouldn't patronize the user. return dimensions preferred_sizes_bonus = 0.4 # reduce the loss by this factor @@ -162,3 +173,15 @@ def get(dimensions: QSize) -> QSize: loss = test_loss return QSize(candidate_width, candidate_height) + +def get_noteable_aspect_ratio(width: float|int, height: float|int) -> tuple[int, int, bool] | None: + """Test whether the aspect_ratio is noteable and return it.""" + aspect_ratio = width / height if height > 0 else 100 + for ar in aspect_ratios: + if abs(ar[2] - aspect_ratio) < 1e-3: + return ar[0], ar[1], (width, height) in _preferred_sizes + elif abs(1/ar[2] - aspect_ratio) < 1e-3: + return ar[1], ar[0], (width, height) in _preferred_sizes + return None + +# notable aspect ratios diff --git a/taggui/widgets/image_list.py b/taggui/widgets/image_list.py index 74212549..594dd63f 100644 --- a/taggui/widgets/image_list.py +++ b/taggui/widgets/image_list.py @@ -6,12 +6,12 @@ from PySide6.QtCore import (QFile, QItemSelection, QItemSelectionModel, QItemSelectionRange, QModelIndex, QSize, QUrl, Qt, - Signal, Slot) -from PySide6.QtGui import QDesktopServices + Signal, Slot, QPersistentModelIndex) +from PySide6.QtGui import QDesktopServices, QColor from PySide6.QtWidgets import (QAbstractItemView, QApplication, QDockWidget, QFileDialog, QHBoxLayout, QLabel, QLineEdit, QListView, QMenu, QMessageBox, QVBoxLayout, - QWidget) + QWidget, QStyledItemDelegate) from pyparsing import (CaselessKeyword, CaselessLiteral, Group, OpAssoc, ParseException, QuotedString, Suppress, Word, infix_notation, nums, one_of, printables) @@ -22,6 +22,8 @@ from utils.settings_widgets import SettingsComboBox from utils.utils import get_confirmation_dialog_reply, pluralize +from taggui.utils.grid import Grid + def replace_filter_wildcards(filter_: str | list) -> str | list: """ @@ -97,6 +99,35 @@ class SelectionMode(str, Enum): TOGGLE = 'Toggle' +class ImageDelegate(QStyledItemDelegate): + def __init__(self, parent=None): + super().__init__(parent) + self.labels = {} + + def sizeHint(self, option, index): + return index.data(Qt.ItemDataRole.SizeHintRole) + + def paint(self, painter, option, index): + super().paint(painter, option, index) + p_index = QPersistentModelIndex(index) + if p_index in self.labels: + label_text = self.labels[p_index] + painter.setBrush(QColor(255, 255, 255, 163)) + painter.drawRect(option.rect) + painter.drawText(option.rect, label_text, Qt.AlignCenter) + + def update_label(self, index: QModelIndex, label: str): + p_index = QPersistentModelIndex(index) + self.labels[p_index] = label + self.parent().update(p_index) + + def remove_label(self, index: QPersistentModelIndex): + p_index = QPersistentModelIndex(index) + if p_index in self.labels: + del self.labels[p_index] + self.parent().update(index) + + class ImageListView(QListView): tags_paste_requested = Signal(list, list) directory_reload_requested = Signal() @@ -107,6 +138,8 @@ def __init__(self, parent, proxy_image_list_model: ProxyImageListModel, self.proxy_image_list_model = proxy_image_list_model self.tag_separator = tag_separator self.setModel(proxy_image_list_model) + self.delegate = ImageDelegate(self) + self.setItemDelegate(self.delegate) self.setWordWrap(True) # If the actual height of the image is greater than 3 times the width, # the image will be scaled down to fit. @@ -168,6 +201,24 @@ def __init__(self, parent, proxy_image_list_model: ProxyImageListModel, def contextMenuEvent(self, event): self.context_menu.exec_(event.globalPos()) + @Slot(Grid) + def show_crop_size(self, grid): + for index in self.selectedIndexes(): + image = index.data(Qt.ItemDataRole.UserRole) + if grid is None: + self.delegate.remove_label(index) + else: + crop_delta = grid.screen.size() - grid.visible.size() + crop_fit = max(crop_delta.width(), crop_delta.height()) + crop_fit_text = f' (-{crop_fit})' if crop_fit > 0 else '' + label = f'image: {image.dimensions[0]}x{image.dimensions[1]}\n'\ + f'crop: {grid.screen.width()}x{grid.screen.height()}{crop_fit_text}\n'\ + f'target: {grid.target.width()}x{grid.target.height()}' + if grid.aspect_ratio is not None: + label += '✅' if grid.aspect_ratio[2] else '' + label += f' {grid.aspect_ratio[0]}:{grid.aspect_ratio[1]}' + self.delegate.update_label(index, label) + @Slot() def invert_selection(self): selected_proxy_rows = {index.row() for index in self.selectedIndexes()} diff --git a/taggui/widgets/image_viewer.py b/taggui/widgets/image_viewer.py index 4078b01c..e08009d9 100644 --- a/taggui/widgets/image_viewer.py +++ b/taggui/widgets/image_viewer.py @@ -1,7 +1,7 @@ from math import ceil, floor, sqrt from PySide6.QtCore import (QModelIndex, QPersistentModelIndex, QPoint, QPointF, QRect, QRectF, QSize, Qt, Signal, Slot) -from PySide6.QtGui import (QAction, QActionGroup, QCursor, QColor, QIcon, +from PySide6.QtGui import (QAction, QActionGroup, QColor, QIcon, QPainter, QPainterPath, QPen, QPixmap, QTransform, QMouseEvent) from PySide6.QtWidgets import (QGraphicsItem, QGraphicsLineItem, @@ -194,7 +194,9 @@ def mouseReleaseEvent(self, event): def paint(self, painter, option, widget=None): if self.rect_type == ImageMarking.CROP: - if self.show_crop_hint and MarkingItem.handle_selected != RectPosition.NONE: + if (self.show_crop_hint and + MarkingItem.handle_selected != RectPosition.NONE and + self==self.scene().mouseGrabberItem()): hint_line_crossings = [ self.rect().center(), self.rect().topLeft() + QPointF(self.rect().width()*golden_ratio, @@ -318,7 +320,8 @@ def __init__(self, boundingRect: QRect, parent=None): super().__init__(parent) self._boundingRect = boundingRect self.rect = QRectF(0, 0, 1, 1) - self.path = QPainterPath() + self.path_ar = QPainterPath() + self.path_size = QPainterPath() self.setCacheMode(QGraphicsItem.DeviceCoordinateCache) self.setZValue(3) @@ -330,7 +333,8 @@ def setValues(self, rect: QRectF, pos: RectPosition): self.rect = rect self.setVisible(pos != RectPosition.NONE) - self.path = QPainterPath() + self.path_ar = QPainterPath() + self.path_size = QPainterPath() if pos == RectPosition.TL: self.add_hyperbola_limit(self.rect.bottomRight(), -1, -1) @@ -354,32 +358,32 @@ def setValues(self, rect: QRectF, pos: RectPosition): def add_line_limit_td(self, x: float, lr: int): width = settings.value('export_resolution', type=int)**2 / self.rect.height() res_size = max(settings.value('export_bucket_res_size', type=int), 1) - self.path.moveTo(x + lr * width, self.rect.y() ) - self.path.lineTo(x + lr * width, self.rect.y() + self.rect.height()) + self.path_size.moveTo(x + lr * width, self.rect.y() ) + self.path_size.lineTo(x + lr * width, self.rect.y() + self.rect.height()) for ar in target_dimension.get_preferred_sizes(): s = max(res_size / ar[0], res_size / ar[1]) f = max(self._boundingRect.width() / ar[0], self._boundingRect.height() / ar[1], 2) - self.path.moveTo(x + lr * ar[0] * s, self.rect.y() + ar[1] * s) - self.path.lineTo(x + lr * ar[0] * f, self.rect.y() + ar[1] * f) - self.path.moveTo(x + lr * ar[0] * s, self.rect.bottom() - ar[1] * s) - self.path.lineTo(x + lr * ar[0] * f, self.rect.bottom() - ar[1] * f) + self.path_ar.moveTo(x + lr * ar[0] * s, self.rect.y() + ar[1] * s) + self.path_ar.lineTo(x + lr * ar[0] * f, self.rect.y() + ar[1] * f) + self.path_ar.moveTo(x + lr * ar[0] * s, self.rect.bottom() - ar[1] * s) + self.path_ar.lineTo(x + lr * ar[0] * f, self.rect.bottom() - ar[1] * f) def add_line_limit_lr(self, y: float, td: int): height = settings.value('export_resolution', type=int)**2 / self.rect.width() res_size = max(settings.value('export_bucket_res_size', type=int), 1) - self.path.moveTo(self.rect.x(), y + td * height) - self.path.lineTo(self.rect.x() + self.rect.width(), y + td * height) + self.path_size.moveTo(self.rect.x(), y + td * height) + self.path_size.lineTo(self.rect.x() + self.rect.width(), y + td * height) for ar in target_dimension.get_preferred_sizes(): s = max(res_size / ar[0], res_size / ar[1]) f = max(self._boundingRect.width() / ar[0], self._boundingRect.height() / ar[1], 2) - self.path.moveTo(self.rect.x() + ar[0] * s, y + td * ar[1] * s) - self.path.lineTo(self.rect.x() + ar[0] * f, y + td * ar[1] * f) - self.path.moveTo(self.rect.right() - ar[0] * s, y + td * ar[1] * s) - self.path.lineTo(self.rect.right() - ar[0] * f, y + td * ar[1] * f) + self.path_ar.moveTo(self.rect.x() + ar[0] * s, y + td * ar[1] * s) + self.path_ar.lineTo(self.rect.x() + ar[0] * f, y + td * ar[1] * f) + self.path_ar.moveTo(self.rect.right() - ar[0] * s, y + td * ar[1] * s) + self.path_ar.lineTo(self.rect.right() - ar[0] * f, y + td * ar[1] * f) def add_hyperbola_limit(self, pos: QPointF, lr: int, td: int): target_area = settings.value('export_resolution', type=int)**2 @@ -393,7 +397,7 @@ def add_hyperbola_limit(self, pos: QPointF, lr: int, td: int): first = True while x < end_x + 50: p = QPointF(x, pos.y() + td * target_area / (lr * (x - pos.x()))) - self.path.moveTo(p) if first else self.path.lineTo(p) + self.path_size.moveTo(p) if first else self.path_size.lineTo(p) first = False x += 50 @@ -401,8 +405,8 @@ def add_hyperbola_limit(self, pos: QPointF, lr: int, td: int): s = max(res_size / ar[0], res_size / ar[1]) f = max(self._boundingRect.width() / ar[0], self._boundingRect.height() / ar[1], 2) - self.path.moveTo(pos.x() + lr * ar[0] * s, pos.y() + td * ar[1] * s) - self.path.lineTo(pos.x() + lr * ar[0] * f, pos.y() + td * ar[1] * f) + self.path_ar.moveTo(pos.x() + lr * ar[0] * s, pos.y() + td * ar[1] * s) + self.path_ar.lineTo(pos.x() + lr * ar[0] * f, pos.y() + td * ar[1] * f) def boundingRect(self): return self._boundingRect @@ -413,10 +417,14 @@ def paint(self, painter, option, widget=None): painter.setClipPath(clip_path) pen = QPen(QColor(255, 255, 255, 127), 3 / self.zoom_factor) painter.setPen(pen) - painter.drawPath(self.path) + painter.drawPath(self.path_size) + painter.drawPath(self.path_ar) + pen = QPen(QColor(0, 255, 0), 1 / self.zoom_factor) + painter.setPen(pen) + painter.drawPath(self.path_size) pen = QPen(QColor(0, 0, 0), 1 / self.zoom_factor) painter.setPen(pen) - painter.drawPath(self.path) + painter.drawPath(self.path_ar) class ImageGraphicsView(QGraphicsView): def __init__(self, scene, image_viewer): @@ -574,25 +582,27 @@ def keyPressEvent(self, event): # Delete marking only when not editing the label self.image_viewer.delete_markings() else: + if MarkingItem.handle_selected == RectPosition.NONE: + if ((event.modifiers() & Qt.KeyboardModifier.ControlModifier) == + Qt.KeyboardModifier.ControlModifier): + if ((event.modifiers() & Qt.KeyboardModifier.AltModifier) == + Qt.KeyboardModifier.AltModifier): + self.set_insertion_mode(ImageMarking.EXCLUDE) + else: + self.set_insertion_mode(ImageMarking.HINT) + super().keyPressEvent(event) + + def keyReleaseEvent(self, event): + if MarkingItem.handle_selected == RectPosition.NONE: if ((event.modifiers() & Qt.KeyboardModifier.ControlModifier) == - Qt.KeyboardModifier.ControlModifier): + Qt.KeyboardModifier.ControlModifier): if ((event.modifiers() & Qt.KeyboardModifier.AltModifier) == Qt.KeyboardModifier.AltModifier): self.set_insertion_mode(ImageMarking.EXCLUDE) else: self.set_insertion_mode(ImageMarking.HINT) - super().keyPressEvent(event) - - def keyReleaseEvent(self, event): - if ((event.modifiers() & Qt.KeyboardModifier.ControlModifier) == - Qt.KeyboardModifier.ControlModifier): - if ((event.modifiers() & Qt.KeyboardModifier.AltModifier) == - Qt.KeyboardModifier.AltModifier): - self.set_insertion_mode(ImageMarking.EXCLUDE) else: - self.set_insertion_mode(ImageMarking.HINT) - else: - self.set_insertion_mode(ImageMarking.NONE) + self.set_insertion_mode(ImageMarking.NONE) super().keyReleaseEvent(event) def resizeEvent(self, event): @@ -605,6 +615,7 @@ class ImageViewer(QWidget): zoom = Signal(float, name='zoomChanged') marking = Signal(ImageMarking, name='markingToAdd') accept_crop_addition = Signal(bool, name='allowAdditionOfCrop') + crop_changed = Signal(Grid, name='cropChanged') def __init__(self, proxy_image_list_model: ProxyImageListModel): super().__init__() @@ -682,6 +693,9 @@ def setting_change(self, key, value): def recalculate_markings(self, ignore: MarkingItem | None = None): if self.crop_marking: calculate_grid(self.crop_marking.rect().toRect()) + if MarkingItem.handle_selected != RectPosition.NONE: + # currently editing the crop marking -> update display + self.crop_changed.emit(grid) else: calculate_grid(MarkingItem.image_size) for marking in self.marking_items: @@ -827,7 +841,7 @@ def add_rectangle(self, rect: QRect, rect_type: ImageMarking, self.accept_crop_addition.emit(False) @Slot() - def label_changed(self, do_emit = True): + def label_changed(self): """Slot to call when a marking label was changed to sync the information in the image.""" self.proxy_image_index.model().sourceModel().add_to_undo_stack( @@ -855,6 +869,7 @@ def marking_changed(self, marking: QGraphicsRectItem): image.crop = marking.rect().toRect() # ensure int! image.target_dimension = grid.target self.inhibit_reload_image = True + self.crop_changed.emit(None) self.proxy_image_list_model.sourceModel().dataChanged.emit( self.proxy_image_index, self.proxy_image_index, [Qt.ItemDataRole.DecorationRole, Qt.ItemDataRole.SizeHintRole, @@ -895,6 +910,6 @@ def delete_markings(self, items: list[MarkingItem] | None = None): Qt.ToolTipRole, Qt.ItemDataRole.UserRole]) else: self.marking_items.remove(item) - self.label_changed(False) + self.label_changed() self.proxy_image_list_model.sourceModel().write_meta_to_disk(image) self.scene.removeItem(item) diff --git a/taggui/widgets/main_window.py b/taggui/widgets/main_window.py index 891d5c09..c44a150d 100644 --- a/taggui/widgets/main_window.py +++ b/taggui/widgets/main_window.py @@ -618,6 +618,7 @@ def connect_image_list_signals(self): self.image_list.visibilityChanged.connect( lambda: self.toggle_image_list_action.setChecked( self.image_list.isVisible())) + self.image_viewer.crop_changed.connect(self.image_list.list_view.show_crop_size) @Slot() def update_image_tags(self): From 944adf20de430c8013710afa0fe581856409b1ab Mon Sep 17 00:00:00 2001 From: StableLlama Date: Sat, 22 Mar 2025 14:08:35 +0100 Subject: [PATCH 44/82] Update readme --- README.md | 46 +++++++++++++++++++++++-------- images/doc/cropping.jpg | Bin 0 -> 294020 bytes taggui/utils/target_dimension.py | 2 -- 3 files changed, 35 insertions(+), 13 deletions(-) create mode 100644 images/doc/cropping.jpg diff --git a/README.md b/README.md index f2447681..dbb05496 100644 --- a/README.md +++ b/README.md @@ -272,28 +272,52 @@ they can be changed into each other. All markings are marking the pixels inside the border 🞑, not any pixels below the border. +![cropping.jpg](images/doc/cropping.jpg) + ### Crop -The _crop_, shown with a blue border 🞑, defines +The _crop_, shown with a blue border, defines the part of the image that will be exported. Depending on the export settings -likely a bucketing is configured. When the cropped area doesn't exactly fit +a bucketing is likely configured. When the cropped area doesn't exactly fit into a bucket as defined by the _Bucket resolution size_ and the _Bucket fitting strategy_, it might be necessary to crop even more. This additional cropped area is shown by a semitransparent red overlay. +During the editing of the crop, you get hints to help with this task: + +In the image list, an overlay shows the size of the original images as well as +the size of the cropped area. The small number in the brackets shows how many +pixels the crop is extended to fit into a bucket. The target size is the image +size when exported, a checkmark is shown when this size is one of the preferred +sizes. And when the crop has a well-known aspect ratio, it is also shown. + +In the main image, lines are shown to quickly be able to select the best size. +The straight lines follow well-known aspect ratios. And a green line shows where +the size of the crop is big enough to fully use the native _Resolution_ of the +model. +These lines are intended for a quick orientation and thus placed at the +theoretical optimal position, taking the discrete nature of image pixels into +account the real optimal position might be in a slightly different place. +Especially when the _Bucket resolution size_ is changed it might be necessary +to optimize the crop when a pixel-perfect result is required. + +Inside the cropping area lines are shown to help with aesthetical alignment +of the content. These lines are drawn in the middle (stroked), following the +1/3rd rule (dashed) and in the golden ratio (dotted). +These lines can temporarily be hidden by pressing the `alt` key. + ### Hint -A _hint_, shown with a gray border 🞑, is just -a hint and has no effect on exporting the image. A _hint_ has a label where -you can give it a name and which you can use for filtering images which contain -the given marking. +A _hint_, shown with a gray border, is just a hint and has no effect on +exporting the image. +A _hint_ has a label where you can give it a name and which you can use for +filtering images which contain the given marking. A _hint_ can be changed in an _exclude_ or an _include_. ### Exclude -An _exclude_, shown with a red border 🞑, is -an area that is guaranteed to be masked (made transparent) when the image is -exported. +An _exclude_, shown with a red border, is an area guaranteed to be masked +(made transparent) when the image is exported. When _Latent size_ and _Quantize alpha channel_ are set and the _exclude_ area doesn't fit, the mask will be grown to make sure that no excluded pixel will stay unmasked. @@ -301,8 +325,8 @@ An _exclude_ can be changed in an _include_ or a _hint_. ### Include -An _include_, shown with a green border 🞑, is -an area that is included when the image is exported. +An _include_, shown with a green border, is an area included when the image is +exported. When no _include_ is set, the full image (of course respecting the _crop_) is included. When an _include_ and an _exclude_ are overlapping, the _exclude_ takes diff --git a/images/doc/cropping.jpg b/images/doc/cropping.jpg new file mode 100644 index 0000000000000000000000000000000000000000..8bd00b14758d3e8146c183444978ed1d7fc078f0 GIT binary patch literal 294020 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

k1kKX$4z<=ydWAR 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 TMn>&tr>vTl3aT-le^&kjm`I6R literal 0 HcmV?d00001 diff --git a/taggui/utils/target_dimension.py b/taggui/utils/target_dimension.py index b2c0f3ad..4cc2df05 100644 --- a/taggui/utils/target_dimension.py +++ b/taggui/utils/target_dimension.py @@ -183,5 +183,3 @@ def get_noteable_aspect_ratio(width: float|int, height: float|int) -> tuple[int, elif abs(1/ar[2] - aspect_ratio) < 1e-3: return ar[1], ar[0], (width, height) in _preferred_sizes return None - -# notable aspect ratios From 36e2d6b41b2ae69fdd698fdc1874b62dc4b60334 Mon Sep 17 00:00:00 2001 From: StableLlama Date: Sat, 22 Mar 2025 14:25:10 +0100 Subject: [PATCH 45/82] Revert local changes --- taggui/run_gui.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/taggui/run_gui.py b/taggui/run_gui.py index 68ecece9..5f19d722 100644 --- a/taggui/run_gui.py +++ b/taggui/run_gui.py @@ -49,7 +49,7 @@ def run_gui(): try: run_gui() except Exception as exception: - #settings.clear() + settings.clear() error_message_box = QMessageBox() error_message_box.setWindowTitle('Error') error_message_box.setIcon(QMessageBox.Icon.Critical) From bc70f97c0ec8f0522c2163b1f5200c37d35f580a Mon Sep 17 00:00:00 2001 From: StableLlama Date: Mon, 24 Mar 2025 23:33:44 +0100 Subject: [PATCH 46/82] Add auto marking by using YOLO models --- .../auto_captioning/auto_captioning_model.py | 15 +- taggui/auto_captioning/captioning_thread.py | 121 ++------- taggui/auto_captioning/models/wd_tagger.py | 12 +- taggui/auto_marking/marking_thread.py | 63 +++++ .../dialogs/caption_multiple_images_dialog.py | 7 +- taggui/dialogs/settings_dialog.py | 41 ++- taggui/models/image_list_model.py | 28 +- taggui/utils/ModelThread.py | 133 +++++++++ taggui/utils/image.py | 1 + taggui/utils/settings.py | 1 + taggui/widgets/auto_markings.py | 257 ++++++++++++++++++ taggui/widgets/icons.py | 52 ++++ taggui/widgets/image_viewer.py | 38 ++- taggui/widgets/main_window.py | 77 +++--- 14 files changed, 664 insertions(+), 182 deletions(-) create mode 100644 taggui/auto_marking/marking_thread.py create mode 100644 taggui/utils/ModelThread.py create mode 100644 taggui/widgets/auto_markings.py create mode 100644 taggui/widgets/icons.py diff --git a/taggui/auto_captioning/auto_captioning_model.py b/taggui/auto_captioning/auto_captioning_model.py index f502d12a..26492a4a 100644 --- a/taggui/auto_captioning/auto_captioning_model.py +++ b/taggui/auto_captioning/auto_captioning_model.py @@ -172,19 +172,8 @@ def monkey_patch_after_loading(self): return @staticmethod - def get_captioning_start_datetime_string( - captioning_start_datetime: datetime) -> str: - return captioning_start_datetime.strftime('%Y-%m-%d %H:%M:%S') - - def get_captioning_message(self, are_multiple_images_selected: bool, - captioning_start_datetime: datetime) -> str: - if are_multiple_images_selected: - captioning_start_datetime_string = ( - self.get_captioning_start_datetime_string( - captioning_start_datetime)) - return (f'Captioning... (device: {self.device}, start time: ' - f'{captioning_start_datetime_string})') - return f'Captioning... (device: {self.device})' + def get_generation_text() -> str: + return 'Captioning' @staticmethod def get_default_prompt() -> str: diff --git a/taggui/auto_captioning/captioning_thread.py b/taggui/auto_captioning/captioning_thread.py index 59e6daef..b61d45b2 100644 --- a/taggui/auto_captioning/captioning_thread.py +++ b/taggui/auto_captioning/captioning_thread.py @@ -1,9 +1,6 @@ -from datetime import datetime from pathlib import Path -from time import perf_counter -from PIL import UnidentifiedImageError -from PySide6.QtCore import QModelIndex, QThread, Qt, Signal +from PySide6.QtCore import QModelIndex, Signal from auto_captioning.auto_captioning_model import AutoCaptioningModel from auto_captioning.models_list import get_model_class @@ -11,6 +8,7 @@ from utils.enums import CaptionPosition from utils.image import Image from utils.settings import get_tag_separator +from utils.ModelThread import ModelThread def add_caption_to_tags(tags: list[str], caption: str, @@ -36,113 +34,46 @@ def add_caption_to_tags(tags: list[str], caption: str, return tags -def format_duration(seconds: float) -> str: - seconds_per_minute = 60 - seconds_per_hour = 60 * seconds_per_minute - seconds_per_day = 24 * seconds_per_hour - if seconds < seconds_per_minute: - return f'{seconds:.1f} seconds' - if seconds < seconds_per_hour: - minutes = seconds / seconds_per_minute - return f'{minutes:.1f} minutes' - if seconds < seconds_per_day: - hours = seconds / seconds_per_hour - return f'{hours:.1f} hours' - days = seconds / seconds_per_day - return f'{days:.1f} days' - - -class CaptioningThread(QThread): - text_outputted = Signal(str) - clear_console_text_edit_requested = Signal() +class CaptioningThread(ModelThread): # The image index, the caption, and the tags with the caption added. The # third parameter must be declared as `list` instead of `list[str]` for it # to work. caption_generated = Signal(QModelIndex, str, list) - progress_bar_update_requested = Signal(int) def __init__(self, parent, image_list_model: ImageListModel, selected_image_indices: list[QModelIndex], caption_settings: dict, tag_separator: str, models_directory_path: Path | None): - super().__init__(parent) - self.image_list_model = image_list_model - self.selected_image_indices = selected_image_indices + super().__init__(parent, image_list_model, selected_image_indices) self.caption_settings = caption_settings self.tag_separator = tag_separator self.models_directory_path = models_directory_path - self.is_error = False - self.is_canceled = False + self.model: AutoCaptioningModel | None = None - def run_captioning(self): + def load_model(self): model_id = self.caption_settings['model_id'] model_class = get_model_class(model_id) - model: AutoCaptioningModel = model_class( + self.model = model_class( captioning_thread_=self, caption_settings=self.caption_settings) - error_message = model.get_error_message() - if error_message: - self.is_error = True - self.clear_console_text_edit_requested.emit() - print(error_message) - return - model.load_processor_and_model() - model.monkey_patch_after_loading() - if self.is_canceled: - print('Canceled captioning.') + self.error_message = self.model.get_error_message() + if self.error_message: return - self.clear_console_text_edit_requested.emit() - selected_image_count = len(self.selected_image_indices) - are_multiple_images_selected = selected_image_count > 1 - captioning_start_datetime = datetime.now() - captioning_message = model.get_captioning_message( - are_multiple_images_selected, captioning_start_datetime) - print(captioning_message) - caption_position = self.caption_settings['caption_position'] - for i, image_index in enumerate(self.selected_image_indices): - start_time = perf_counter() - if self.is_canceled: - print('Canceled captioning.') - return - image: Image = self.image_list_model.data(image_index, - Qt.ItemDataRole.UserRole) - image_prompt = model.get_image_prompt(image) - try: - model_inputs = model.get_model_inputs(image_prompt, image) - except UnidentifiedImageError: - print(f'Skipping {image.path.name} because its file format is ' - 'not supported or it is a corrupted image.') - continue - caption, console_output_caption = model.generate_caption( - model_inputs, image_prompt) - tags = add_caption_to_tags(image.tags, caption, caption_position) - self.caption_generated.emit(image_index, caption, tags) - if are_multiple_images_selected: - self.progress_bar_update_requested.emit(i + 1) - if i == 0 and not are_multiple_images_selected: - self.clear_console_text_edit_requested.emit() - if console_output_caption is None: - console_output_caption = caption - print(f'{image.path.name} ({perf_counter() - start_time:.1f} s):\n' - f'{console_output_caption}') - if are_multiple_images_selected: - captioning_end_datetime = datetime.now() - total_captioning_duration = ((captioning_end_datetime - - captioning_start_datetime) - .total_seconds()) - average_captioning_duration = (total_captioning_duration / - selected_image_count) - print(f'Finished captioning {selected_image_count} images in ' - f'{format_duration(total_captioning_duration)} ' - f'({average_captioning_duration:.1f} s/image) at ' - f'{captioning_end_datetime.strftime("%Y-%m-%d %H:%M:%S")}.') + self.model.load_processor_and_model() + self.model.monkey_patch_after_loading() + self.device = self.model.device + self.text = { + 'Generating': self.model.get_generation_text(), + 'generating': 'captioning' + } - def run(self): - try: - self.run_captioning() - except Exception as exception: - self.is_error = True - # Show the error message in the console text edit. - raise exception + def get_model_inputs(self, image: Image): + image_prompt = self.model.get_image_prompt(image) + return image_prompt, self.model.get_model_inputs(image_prompt, image) - def write(self, text: str): - self.text_outputted.emit(text) + def generate_output(self, image_index, image: Image, image_prompt: str | None, model_inputs) -> str: + caption_position = self.caption_settings['caption_position'] + caption, console_output_caption = self.model.generate_caption( + model_inputs, image_prompt) + tags = add_caption_to_tags(image.tags, caption, caption_position) + self.caption_generated.emit(image_index, caption, tags) + return console_output_caption diff --git a/taggui/auto_captioning/models/wd_tagger.py b/taggui/auto_captioning/models/wd_tagger.py index 2b5f3619..5745af15 100644 --- a/taggui/auto_captioning/models/wd_tagger.py +++ b/taggui/auto_captioning/models/wd_tagger.py @@ -108,15 +108,9 @@ def get_processor(self): def get_model(self): return WdTaggerModel(self.model_id) - def get_captioning_message(self, are_multiple_images_selected: bool, - captioning_start_datetime: datetime) -> str: - if are_multiple_images_selected: - captioning_start_datetime_string = ( - self.get_captioning_start_datetime_string( - captioning_start_datetime)) - return (f'Generating tags... (start time: ' - f'{captioning_start_datetime_string})') - return 'Generating tags...' + @staticmethod + def get_generation_text() -> str: + return 'Generating tags' def get_model_inputs(self, image_prompt: str, image: Image) -> np.ndarray: pil_image = self.load_image(image) diff --git a/taggui/auto_marking/marking_thread.py b/taggui/auto_marking/marking_thread.py new file mode 100644 index 00000000..8baf3000 --- /dev/null +++ b/taggui/auto_marking/marking_thread.py @@ -0,0 +1,63 @@ +from PySide6.QtCore import QModelIndex, QThread, Signal, Qt + +from ultralytics import YOLO + +from models.image_list_model import ImageListModel +from utils.image import Image +from utils.ModelThread import ModelThread + + +class MarkingThread(ModelThread): + # The image index, the caption, and the tags with the caption added. The + # third parameter must be declared as `list` instead of `list[str]` for it + # to work. + marking_generated = Signal(QModelIndex, list) + + def __init__(self, parent, image_list_model: ImageListModel, + selected_image_indices: list[QModelIndex], + marking_settings: dict): + super().__init__(parent, image_list_model, selected_image_indices) + self.marking_settings = marking_settings + self.model: YOLO | None = None + self.text = { + 'Generating': 'Marking', + 'generating': 'marking' + } + + def load_model(self): + if not self.model: + self.error_message = 'Model not preloaded.' + self.is_error = True + pass + + def preload_model(self): + self.model = YOLO(self.marking_settings['model_path']) + if self.error_message: + return + + def get_model_inputs(self, image: Image): + return '', {} + + def generate_output(self, image_index, image: Image, image_prompt, model_inputs) -> str: + if len(self.marking_settings['classes']) == 0: + return 'No classes to mark selected.' + classes = list(self.marking_settings['classes'].keys()) + results = self.model.predict(source=image.path, + conf=self.marking_settings['conf'], + iou=self.marking_settings['iou'], + max_det=self.marking_settings['max_det'], + classes=classes, + retina_masks=True) + markings = [] + for r in results: + for box, class_id, confidence in zip(r.boxes.xyxy.to('cpu').tolist(), + r.boxes.cls.to('cpu').tolist(), + r.boxes.conf.to('cpu').tolist()): + marking = self.marking_settings['classes'].get(class_id) + if marking is not None: + markings.append({'box': box, + 'label': marking[0], + 'type': marking[1], + 'confidence': confidence}) + self.marking_generated.emit(image_index, markings) + return f'Found {len(markings)} marking(s).' diff --git a/taggui/dialogs/caption_multiple_images_dialog.py b/taggui/dialogs/caption_multiple_images_dialog.py index 13abbe0e..0b3e4fee 100644 --- a/taggui/dialogs/caption_multiple_images_dialog.py +++ b/taggui/dialogs/caption_multiple_images_dialog.py @@ -3,9 +3,10 @@ class CaptionMultipleImagesDialog(ConfirmationDialog): - def __init__(self, selected_image_count: int): - title = 'Generate Captions' - question = f'Caption {selected_image_count} selected images?' + def __init__(self, selected_image_count: int, caption_singular = 'Caption', + caption_plural = 'Captions'): + title = f'Generate {caption_plural}' + question = f'{caption_singular} {selected_image_count} selected images?' super().__init__(title=title, question=question) self.show_alert_check_box = SettingsBigCheckBox( key='show_alert_when_captioning_finished', default=True, diff --git a/taggui/dialogs/settings_dialog.py b/taggui/dialogs/settings_dialog.py index 4541865e..6df24873 100644 --- a/taggui/dialogs/settings_dialog.py +++ b/taggui/dialogs/settings_dialog.py @@ -30,11 +30,17 @@ def __init__(self, parent): 5, 0, Qt.AlignmentFlag.AlignRight) grid_layout.addWidget(QLabel('Auto-captioning models directory'), 6, 0, Qt.AlignmentFlag.AlignRight) + grid_layout.addWidget(QLabel('Auto-marking models directory'), 8, 0, + Qt.AlignmentFlag.AlignRight) font_size_spin_box = SettingsSpinBox( key='font_size', minimum=1, maximum=99) font_size_spin_box.valueChanged.connect(self.show_restart_warning) + file_types_line_edit = SettingsLineEdit( + key='image_list_file_formats') + file_types_line_edit.setMinimumWidth(400) + file_types_line_edit.textChanged.connect(self.show_restart_warning) # Images that are too small cause lag, so set a minimum width. image_list_image_width_spin_box = SettingsSpinBox( key='image_list_image_width', @@ -70,10 +76,15 @@ def __init__(self, parent): models_directory_button.setFixedWidth( int(models_directory_button.sizeHint().width() * 1.3)) models_directory_button.clicked.connect(self.set_models_directory_path) - file_types_line_edit = SettingsLineEdit( - key='image_list_file_formats') - file_types_line_edit.setMinimumWidth(400) - file_types_line_edit.textChanged.connect(self.show_restart_warning) + self.marking_models_directory_line_edit = SettingsLineEdit( + key='marking_models_directory_path') + self.marking_models_directory_line_edit.setMinimumWidth(400) + self.marking_models_directory_line_edit.setClearButtonEnabled(True) + marking_models_directory_button = QPushButton('Select Directory...') + marking_models_directory_button.setFixedWidth( + int(marking_models_directory_button.sizeHint().width() * 1.3)) + marking_models_directory_button.clicked.connect( + self.set_marking_models_directory_path) grid_layout.addWidget(font_size_spin_box, 0, 1, Qt.AlignmentFlag.AlignLeft) @@ -91,6 +102,10 @@ def __init__(self, parent): Qt.AlignmentFlag.AlignLeft) grid_layout.addWidget(models_directory_button, 7, 1, Qt.AlignmentFlag.AlignLeft) + grid_layout.addWidget(self.marking_models_directory_line_edit, 8, 1, + Qt.AlignmentFlag.AlignLeft) + grid_layout.addWidget(marking_models_directory_button, 9, 1, + Qt.AlignmentFlag.AlignLeft) layout.addLayout(grid_layout) # Prevent the grid layout from moving to the center when the warning @@ -147,3 +162,21 @@ def set_models_directory_path(self): dir=initial_directory_path) if models_directory_path: self.models_directory_line_edit.setText(models_directory_path) + + @Slot() + def set_marking_models_directory_path(self): + marking_models_directory_path = settings.value( + 'marking_models_directory_path', + defaultValue=DEFAULT_SETTINGS['marking_models_directory_path'], type=str) + if marking_models_directory_path: + initial_directory_path = marking_models_directory_path + elif settings.contains('directory_path'): + initial_directory_path = settings.value('directory_path') + else: + initial_directory_path = '' + marking_models_directory_path = QFileDialog.getExistingDirectory( + parent=self, caption='Select directory containing auto-marking ' + 'models (YOLO models)', + dir=initial_directory_path) + if marking_models_directory_path: + self.marking_models_directory_line_edit.setText(marking_models_directory_path) diff --git a/taggui/models/image_list_model.py b/taggui/models/image_list_model.py index 54b397b9..a470ce23 100644 --- a/taggui/models/image_list_model.py +++ b/taggui/models/image_list_model.py @@ -4,6 +4,7 @@ from collections import Counter, deque from dataclasses import dataclass from enum import Enum +from math import floor, ceil from pathlib import Path import json @@ -202,9 +203,10 @@ def load_directory(self, directory_path: Path): markings = meta.get('markings') if markings and type(markings) is list: for marking in markings: - marking = Marking(marking.get('label'), - ImageMarking[marking.get('type')], - QRect(*marking.get('rect'))) + marking = Marking(label=marking.get('label'), + type=ImageMarking[marking.get('type')], + rect=QRect(*marking.get('rect')), + confidence=marking.get('confidence', 1.0)) image.markings.append(marking) else: error_messages.append(f'Invalid version ' @@ -249,8 +251,10 @@ def write_meta_to_disk(self, image: Image): meta: dict[str, any] = {'version': 1} if image.crop is not None: meta['crop'] = image.crop.getRect() + print('image.markings', image.markings) meta['markings'] = [{'label': marking.label, 'type': marking.type.name, + 'confidence': marking.confidence, 'rect': marking.rect.getRect()} for marking in image.markings] if does_exist or len(meta.keys()) > 1: try: @@ -627,3 +631,21 @@ def delete_tags(self, tags: list[str], if changed_image_indices: self.dataChanged.emit(self.index(changed_image_indices[0]), self.index(changed_image_indices[-1])) + + def add_image_markings(self, image_index: QModelIndex, markings: list[dict]): + image: Image = self.data(image_index, Qt.ItemDataRole.UserRole) + for marking in markings: + marking_type = { + 'hint': ImageMarking.HINT, + 'include': ImageMarking.INCLUDE, + 'exclude': ImageMarking.EXCLUDE}[marking['type']] + box = marking['box'] + top_left = QPoint(floor(box[0]), floor(box[1])) + bottom_right = QPoint(ceil(box[2]), ceil(box[3])) + image.markings.append(Marking(label=marking['label'], + type=marking_type, + rect=QRect(top_left, bottom_right), + confidence=marking['confidence'])) + if len(markings) > 0: + self.dataChanged.emit(image_index, image_index) + self.write_meta_to_disk(image) diff --git a/taggui/utils/ModelThread.py b/taggui/utils/ModelThread.py new file mode 100644 index 00000000..4b1e3a3f --- /dev/null +++ b/taggui/utils/ModelThread.py @@ -0,0 +1,133 @@ +from abc import abstractmethod +from datetime import datetime +from time import perf_counter + +import numpy as np +from PIL import UnidentifiedImageError +from transformers import BatchFeature +from PySide6.QtCore import QModelIndex, QThread, Qt, Signal + +from utils.image import Image +from models.image_list_model import ImageListModel + +def format_duration(seconds: float) -> str: + seconds_per_minute = 60 + seconds_per_hour = 60 * seconds_per_minute + seconds_per_day = 24 * seconds_per_hour + if seconds < seconds_per_minute: + return f'{seconds:.1f} seconds' + if seconds < seconds_per_hour: + minutes = seconds / seconds_per_minute + return f'{minutes:.1f} minutes' + if seconds < seconds_per_day: + hours = seconds / seconds_per_hour + return f'{hours:.1f} hours' + days = seconds / seconds_per_day + return f'{days:.1f} days' + + +class ModelThread(QThread): + """Base class for all model running threads""" + text_outputted = Signal(str) + clear_console_text_edit_requested = Signal() + progress_bar_update_requested = Signal(int) + + def __init__(self, parent, image_list_model: ImageListModel, + selected_image_indices: list[QModelIndex]): + super().__init__(parent) + self.image_list_model = image_list_model + self.selected_image_indices = selected_image_indices + self.is_error = False + self.error_message = '' + self.is_canceled = False + self.device = 'default' + self.text = { + 'Generating': 'Generating', + 'generating': 'generating' + } + + def run_generating(self): + self.load_model() + if self.is_error: + self.clear_console_text_edit_requested.emit() + print(self.error_message) + return + if self.is_canceled: + print(f'Canceled {self.text['generating']}.') + return + self.clear_console_text_edit_requested.emit() + selected_image_count = len(self.selected_image_indices) + are_multiple_images_selected = selected_image_count > 1 + generating_start_datetime = datetime.now() + generating_message = self.get_generating_message( + are_multiple_images_selected, generating_start_datetime) + print(generating_message) + for i, image_index in enumerate(self.selected_image_indices): + start_time = perf_counter() + if self.is_canceled: + print(f'Canceled {self.text['generating']}.') + return + image: Image = self.image_list_model.data(image_index, + Qt.ItemDataRole.UserRole) + try: + image_prompt, model_inputs = self.get_model_inputs(image) + except UnidentifiedImageError: + print(f'Skipping {image.path.name} because its file format is ' + 'not supported or it is a corrupted image.') + continue + console_output_caption = self.generate_output(image_index, image, + image_prompt, model_inputs) + if are_multiple_images_selected: + self.progress_bar_update_requested.emit(i + 1) + if i == 0 and not are_multiple_images_selected: + self.clear_console_text_edit_requested.emit() + print(f'{image.path.name} ({perf_counter() - start_time:.1f} s):\n' + f'{console_output_caption}') + if are_multiple_images_selected: + generating_end_datetime = datetime.now() + total_generating_duration = ((generating_end_datetime + - generating_start_datetime) + .total_seconds()) + average_generating_duration = (total_generating_duration / + selected_image_count) + print(f'Finished {self.text['generating']} {selected_image_count} images in ' + f'{format_duration(total_generating_duration)} ' + f'({average_generating_duration:.1f} s/image) at ' + f'{generating_end_datetime.strftime("%Y-%m-%d %H:%M:%S")}.') + + @abstractmethod + def load_model(self): + """Load the model for the generating task.""" + pass + + def get_generating_message(self, are_multiple_images_selected: bool, + generating_start_datetime: datetime) -> str: + if are_multiple_images_selected: + generating_start_datetime_string = ( + generating_start_datetime.strftime('%Y-%m-%d %H:%M:%S')) + return (f'{self.text['Generating']}... (device: {self.device}, ' + f'start time: {generating_start_datetime_string})') + return f'{self.text['Generating']}... (device: {self.device})' + + @abstractmethod + def get_model_inputs(self, image: Image) -> tuple[ + str | None, BatchFeature | dict | np.ndarray]: + pass + + @abstractmethod + def generate_output(self, image_index, + image: Image, + image_prompt: str | None, + model_inputs: BatchFeature | dict | np.ndarray) -> str: + pass + + def run(self): + try: + self.run_generating() + except Exception as exception: + self.is_error = True + # Show the error message in the console text edit. + raise exception + + def write(self, text: str): + self.text_outputted.emit(text) diff --git a/taggui/utils/image.py b/taggui/utils/image.py index af5b3b0d..055b8471 100644 --- a/taggui/utils/image.py +++ b/taggui/utils/image.py @@ -19,6 +19,7 @@ class Marking: label: str type: ImageMarking rect: QRect + confidence: float = 1.0 @dataclass diff --git a/taggui/utils/settings.py b/taggui/utils/settings.py index 5673df78..3f0b401b 100644 --- a/taggui/utils/settings.py +++ b/taggui/utils/settings.py @@ -10,6 +10,7 @@ 'insert_space_after_tag_separator': True, 'autocomplete_tags': True, 'models_directory_path': '', + 'marking_models_directory_path': '', 'export_filter': 'All images', 'export_preset': 'SDXL, SD3, Flux', 'export_resolution': 1024, diff --git a/taggui/widgets/auto_markings.py b/taggui/widgets/auto_markings.py new file mode 100644 index 00000000..464da1fd --- /dev/null +++ b/taggui/widgets/auto_markings.py @@ -0,0 +1,257 @@ +import sys +from pathlib import Path + +from PySide6.QtCore import Signal, QModelIndex, Qt, Slot +from PySide6.QtGui import QTextCursor, QFont +from PySide6.QtWidgets import QDockWidget, QProgressBar, QPlainTextEdit, QWidget, QVBoxLayout, QScrollArea, \ + QAbstractScrollArea, QFrame, QFormLayout, QMessageBox, QTableWidget, QHeaderView, QTableWidgetItem, QComboBox + +from models.image_list_model import ImageListModel +from utils.big_widgets import TallPushButton +from utils.settings import settings, DEFAULT_SETTINGS +from utils.settings_widgets import FocusedScrollSettingsComboBox +from widgets.auto_captioner import set_text_edit_height, restore_stdout_and_stderr +from widgets.image_list import ImageList +from auto_marking.marking_thread import MarkingThread +from dialogs.caption_multiple_images_dialog import CaptionMultipleImagesDialog + +from widgets.icons import create_add_box_icon + +from taggui.utils.utils import pluralize + + +class MarkingSettingsForm(QVBoxLayout): + model_selected = Signal(bool) + + def __init__(self): + super().__init__() + basic_settings_form = QFormLayout() + basic_settings_form.setRowWrapPolicy( + QFormLayout.RowWrapPolicy.WrapAllRows) + basic_settings_form.setFieldGrowthPolicy( + QFormLayout.FieldGrowthPolicy.ExpandingFieldsGrow) + self.model_combo_box = FocusedScrollSettingsComboBox(key='marking_model_id') + self.model_combo_box.setPlaceholderText('Set marking model directory in "Settings..."') + self.model_combo_box.activated.connect(lambda _: self.model_selected.emit(True)) + self.get_local_model_paths() + settings.change.connect(lambda key, value: self.get_local_model_paths() + if key == 'marking_models_directory_path' else 0) + basic_settings_form.addRow('Model', self.model_combo_box) + + self.class_table = QTableWidget(0, 2) + self.class_table.setHorizontalHeaderLabels(['Class', 'Marking']) + self.class_table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch) + self.class_table.verticalHeader().setSectionResizeMode(QHeaderView.ResizeToContents) + basic_settings_form.addRow('Classes', self.class_table) + + self.addLayout(basic_settings_form) + + def get_local_model_paths(self): + models_directory_path = settings.value( + 'marking_models_directory_path', + defaultValue=DEFAULT_SETTINGS['marking_models_directory_path'], + type=str) + if not models_directory_path: + return + models_directory_path = Path(models_directory_path) + print(f'Loading local auto-marking model paths under ' + f'{models_directory_path}...') + config_paths = set(models_directory_path.glob('**/*.pt')) + self.model_selected.emit(False) + self.model_combo_box.clear() + if len(config_paths) == 0: + self.model_combo_box.setPlaceholderText( + 'Set marking model directory in "Settings..."') + else: + self.model_combo_box.setPlaceholderText('Select marking model') + for path in config_paths: + self.model_combo_box.addItem( + str(path.relative_to(models_directory_path)), userData=path) + + def get_marking_settings(self) -> dict: + return { + #'model_id': self.model_combo_box.currentText(), + 'model_path': self.model_combo_box.currentData(), + 'conf': 0.25, + 'iou': 0.7, + 'max_det': 300, + 'classes': [] + } + +class AutoMarkings(QDockWidget): + marking_generated = Signal(QModelIndex, list) + + def __init__(self, image_list_model: ImageListModel, + image_list: ImageList, parent): + super().__init__(parent) + self.image_list_model = image_list_model + self.image_list = image_list + self.is_marking = False + self.marking_thread = None + self.show_alert_when_finished = False + # Whether the last block of text in the console text edit should be + # replaced with the next block of text that is outputted. + self.replace_last_console_text_edit_block = False + # Each `QDockWidget` needs a unique object name for saving its state. + self.setObjectName('auto_markings') + self.setWindowTitle('Auto-Markings') + self.setAllowedAreas(Qt.DockWidgetArea.LeftDockWidgetArea + | Qt.DockWidgetArea.RightDockWidgetArea) + + self.start_cancel_button = TallPushButton('Start Auto-Marking') + self.start_cancel_button.setEnabled(False) + self.progress_bar = QProgressBar() + self.progress_bar.setFormat('%v / %m images marked (%p%)') + self.progress_bar.hide() + self.console_text_edit = QPlainTextEdit() + set_text_edit_height(self.console_text_edit, 4) + self.console_text_edit.setReadOnly(True) + self.console_text_edit.hide() + container = QWidget() + layout = QVBoxLayout(container) + layout.addWidget(self.start_cancel_button) + layout.addWidget(self.progress_bar) + layout.addWidget(self.console_text_edit) + self.marking_settings_form = MarkingSettingsForm() + layout.addLayout(self.marking_settings_form) + scroll_area = QScrollArea() + scroll_area.setWidgetResizable(True) + scroll_area.setSizeAdjustPolicy( + QAbstractScrollArea.SizeAdjustPolicy.AdjustToContents) + scroll_area.setFrameShape(QFrame.Shape.NoFrame) + scroll_area.setWidget(container) + self.setWidget(scroll_area) + + self.start_cancel_button.clicked.connect( + self.start_or_cancel_marking) + self.marking_settings_form.model_selected.connect(lambda _: self.prepare_generation()) + self.marking_settings_form.model_selected.connect(self.start_cancel_button.setEnabled) + + @Slot() + def start_or_cancel_marking(self): + if self.is_marking: + # Cancel marking. + self.marking_thread.is_canceled = True + self.start_cancel_button.setEnabled(False) + self.start_cancel_button.setText('Canceling Auto-Marking...') + else: + # Start marking. + self.generate_markings() + + def set_is_marking(self, is_marking: bool): + self.is_marking = is_marking + button_text = ('Cancel Auto-Marking' if is_marking + else 'Start Auto-Marking') + self.start_cancel_button.setText(button_text) + + @Slot(str) + def update_console_text_edit(self, text: str): + # '\x1b[A' is the ANSI escape sequence for moving the cursor up. + if text == '\x1b[A': + self.replace_last_console_text_edit_block = True + return + text = text.strip() + if not text: + return + if self.console_text_edit.isHidden(): + self.console_text_edit.show() + if self.replace_last_console_text_edit_block: + self.replace_last_console_text_edit_block = False + # Select and remove the last block of text. + self.console_text_edit.moveCursor(QTextCursor.MoveOperation.End) + self.console_text_edit.moveCursor( + QTextCursor.MoveOperation.StartOfBlock, + QTextCursor.MoveMode.KeepAnchor) + self.console_text_edit.textCursor().removeSelectedText() + # Delete the newline. + self.console_text_edit.textCursor().deletePreviousChar() + self.console_text_edit.appendPlainText(text) + + @Slot() + def show_alert(self): + if self.marking_thread.is_canceled: + return + if self.marking_thread.is_error: + icon = QMessageBox.Icon.Critical + text = ('An error occurred during marking. See the ' + 'Auto-Marking console for more information.') + else: + icon = QMessageBox.Icon.Information + text = 'Marking has finished.' + alert = QMessageBox() + alert.setIcon(icon) + alert.setText(text) + alert.exec() + + def prepare_generation(self): + selected_image_indices = self.image_list.get_selected_image_indices() + marking_settings = self.marking_settings_form.get_marking_settings() + self.marking_thread = MarkingThread( + self, self.image_list_model, selected_image_indices, + marking_settings) + self.marking_thread.text_outputted.connect( + self.update_console_text_edit) + self.marking_thread.clear_console_text_edit_requested.connect( + self.console_text_edit.clear) + self.marking_thread.marking_generated.connect( + self.marking_generated) + self.marking_thread.progress_bar_update_requested.connect( + self.progress_bar.setValue) + self.marking_thread.finished.connect( + lambda: self.set_is_marking(False)) + self.marking_thread.finished.connect(restore_stdout_and_stderr) + self.marking_thread.finished.connect(self.progress_bar.hide) + self.marking_thread.finished.connect( + lambda: self.start_cancel_button.setEnabled(True)) + if self.show_alert_when_finished: + self.marking_thread.finished.connect(self.show_alert) + self.marking_thread.preload_model() + self.marking_settings_form.class_table.setRowCount( + len(self.marking_thread.model.names)) + for row, (class_id, class_name) in enumerate( + self.marking_thread.model.names.items()): + self.marking_settings_form.class_table.setItem( + row, 0, QTableWidgetItem(class_name)) + combo = QComboBox() + combo.addItem('ignore') + combo.addItem(create_add_box_icon(Qt.gray), 'hint') + combo.addItem(create_add_box_icon(Qt.red), 'exclude') + combo.addItem(create_add_box_icon(Qt.green), 'include') + self.marking_settings_form.class_table.setCellWidget(row, 1, combo) + # Redirect `stdout` and `stderr` so that the outputs are displayed in + # the console text edit. + ###sys.stdout = self.marking_thread + ###sys.stderr = self.marking_thread + + @Slot() + def generate_markings(self): + selected_image_indices = self.image_list.get_selected_image_indices() + if self.marking_thread is None: + self.prepare_generation() + self.marking_thread.selected_image_indices = selected_image_indices + classes = {} + for row, (class_id, class_name) in enumerate( + self.marking_thread.model.names.items()): + combo = self.marking_settings_form.class_table.cellWidget(row, 1).currentText() + if combo != 'ignore': + classes[class_id] = (class_name, combo) + self.marking_thread.marking_settings['classes'] = classes + selected_image_count = len(selected_image_indices) + self.image_list_model.add_to_undo_stack( + action_name=f'Generate ' + f'{pluralize('Marking', selected_image_count)}', + should_ask_for_confirmation=selected_image_count > 1) + if selected_image_count > 1: + confirmation_dialog = CaptionMultipleImagesDialog( + selected_image_count, 'Mark', 'Markings') + reply = confirmation_dialog.exec() + if reply != QMessageBox.StandardButton.Yes: + return + self.show_alert_when_finished = (confirmation_dialog + .show_alert_check_box.isChecked()) + self.set_is_marking(True) + if selected_image_count > 1: + self.progress_bar.setRange(0, selected_image_count) + self.progress_bar.setValue(0) + self.progress_bar.show() + self.marking_thread.start() diff --git a/taggui/widgets/icons.py b/taggui/widgets/icons.py new file mode 100644 index 00000000..9e22f495 --- /dev/null +++ b/taggui/widgets/icons.py @@ -0,0 +1,52 @@ +from pathlib import Path + +from PySide6.QtGui import QColor, QPixmap, QIcon, QPainter, QPen, Qt, QPainterPath, QImage +from PySide6.QtCore import QRect + +from taggui.utils.utils import get_resource_path + +ICON_PATH = Path('images/icon.ico') +TOGGLE_MARKING_ICON_PATH = Path('images/toggle_marking.png') +SHOW_MARKINGS_ICON_PATH = Path('images/show_marking.png') +SHOW_LABELS_ICON_PATH = Path('images/show_label.png') +SHOW_MARKING_LATENT_ICON_PATH = Path('images/show_marking_latent.png') + +def taggui_icon(): + return QIcon(str(get_resource_path(ICON_PATH))) + +def toggle_marking_icon(): + return QIcon(str(get_resource_path(TOGGLE_MARKING_ICON_PATH))) + +def show_markings_icon(): + return QIcon(str(get_resource_path(SHOW_MARKINGS_ICON_PATH))) + +def show_labels_icon(): + return QIcon(str(get_resource_path(SHOW_LABELS_ICON_PATH))) + +def show_marking_latent_icon(): + return QIcon(str(get_resource_path(SHOW_MARKING_LATENT_ICON_PATH))) + +def create_add_box_icon(color: QColor) -> QPixmap: + """Create a QPixmap for an icon""" + pixmap = QPixmap(32, 32) + pixmap.fill(QColor('transparent')) + + # Create a painter to draw on the pixmap + painter = QPainter(pixmap) + + # Draw a bordered rectangle in the specified color + rect = QRect(2, 2, 28, 28) + painter.setPen(QPen(color, 2)) + painter.drawRect(rect) + + # Draw a plus sign in the middle + painter.setPen(QPen(Qt.black, 1)) + path = QPainterPath() + path.moveTo(16, 10) + path.lineTo(16, 22) + path.moveTo(10, 16) + path.lineTo(22, 16) + painter.drawPath(path) + painter.end() + + return pixmap diff --git a/taggui/widgets/image_viewer.py b/taggui/widgets/image_viewer.py index e08009d9..470193eb 100644 --- a/taggui/widgets/image_viewer.py +++ b/taggui/widgets/image_viewer.py @@ -1,3 +1,4 @@ +import re from math import ceil, floor, sqrt from PySide6.QtCore import (QModelIndex, QPersistentModelIndex, QPoint, QPointF, QRect, QRectF, QSize, Qt, Signal, Slot) @@ -282,8 +283,11 @@ def adjust_layout(self): class MarkingLabel(QGraphicsTextItem): editingFinished = Signal() - def __init__(self, text, parent): - super().__init__(text, parent) + def __init__(self, text, confidence, parent): + if 0 <= confidence < 1: + super().__init__(f'{text}: {confidence:.3f}', parent) + else: + super().__init__(text, parent) self.setDefaultTextColor(Qt.black) self.setTextInteractionFlags(Qt.TextEditorInteraction) @@ -681,7 +685,7 @@ def load_image(self, proxy_image_index: QModelIndex, is_complete = True): calculate_grid(MarkingItem.image_size) for marking in image.markings: self.add_rectangle(marking.rect, marking.type, interactive=False, - name=marking.label) + name=marking.label, confidence=marking.confidence) @Slot() def setting_change(self, key, value): @@ -807,7 +811,8 @@ def wheelEvent(self, event): self.view.translate(delta.x(), delta.y()) def add_rectangle(self, rect: QRect, rect_type: ImageMarking, - interactive: bool, size: QSize = None, name: str = ''): + interactive: bool, size: QSize = None, name: str = '', + confidence: float = 1.0): self.marking_to_add = ImageMarking.NONE marking_item = MarkingItem(rect, rect_type, interactive, size) marking_item.setVisible(self.show_marking_state) @@ -819,15 +824,16 @@ def add_rectangle(self, rect: QRect, rect_type: ImageMarking, name = {ImageMarking.HINT: 'hint', ImageMarking.INCLUDE: 'include', ImageMarking.EXCLUDE: 'exclude'}[rect_type] - image.markings.append(Marking(name, rect_type, rect)) + image.markings.append(Marking(name, rect_type, rect, confidence)) marking_item.setData(0, name) + marking_item.setData(1, confidence) if rect_type != ImageMarking.CROP and rect_type != ImageMarking.NONE: label_background = QGraphicsRectItem(marking_item) label_background.setZValue(2) label_background.setBrush(marking_item.color) label_background.setPen(Qt.NoPen) label_background.setVisible(self.show_label_state) - marking_item.label = MarkingLabel(name, label_background) + marking_item.label = MarkingLabel(name, confidence, label_background) marking_item.label.setZValue(2) marking_item.label.setVisible(self.show_label_state) marking_item.label.editingFinished.connect(self.label_changed) @@ -850,10 +856,19 @@ def label_changed(self): image.markings.clear() for marking in self.marking_items: if marking.rect_type != ImageMarking.CROP: - marking.label.parentItem().parentItem().setData(0, marking.label.toPlainText()) - image.markings.append(Marking(marking.data(0), - marking.rect_type, - marking.rect().toRect())) + label = marking.label.toPlainText() + match = re.match(r'^(.*):\s*(\d*\.\d+)$', label) + if match: + label = match.group(1) + confidence = float(match.group(2)) + else: + confidence = 1.0 + marking.label.parentItem().parentItem().setData(0, label) + marking.label.parentItem().parentItem().setData(1, confidence) + image.markings.append(Marking(label=label, + type=marking.rect_type, + rect=marking.rect().toRect(), + confidence=confidence)) self.proxy_image_list_model.sourceModel().write_meta_to_disk(image) @Slot(QGraphicsRectItem) @@ -878,7 +893,8 @@ def marking_changed(self, marking: QGraphicsRectItem): else: image.markings = [Marking(m.data(0), m.rect_type, - m.rect().toRect()) + m.rect().toRect(), + m.data(1)) for m in self.marking_items if m.rect_type != ImageMarking.CROP] self.proxy_image_list_model.sourceModel().write_meta_to_disk(image) diff --git a/taggui/widgets/main_window.py b/taggui/widgets/main_window.py index c44a150d..b3d6d335 100644 --- a/taggui/widgets/main_window.py +++ b/taggui/widgets/main_window.py @@ -1,10 +1,8 @@ from pathlib import Path -from PySide6.QtCore import (QKeyCombination, QModelIndex, QPoint, QRect, QUrl, - Qt, Slot) -from PySide6.QtGui import (QAction, QActionGroup, QColor, QCloseEvent, - QDesktopServices, QIcon, QKeySequence, QPainter, - QPainterPath, QPen, QPixmap, QShortcut) +from PySide6.QtCore import QKeyCombination, QModelIndex, QUrl, Qt, Slot +from PySide6.QtGui import (QAction, QActionGroup, QCloseEvent, QDesktopServices, + QIcon, QKeySequence, QShortcut) from PySide6.QtWidgets import (QApplication, QFileDialog, QMainWindow, QMessageBox, QStackedWidget, QToolBar, QVBoxLayout, QWidget) @@ -27,15 +25,15 @@ from utils.utils import get_resource_path, pluralize from widgets.all_tags_editor import AllTagsEditor from widgets.auto_captioner import AutoCaptioner +from widgets.auto_markings import AutoMarkings from widgets.image_list import ImageList from widgets.image_tags_editor import ImageTagsEditor from widgets.image_viewer import ImageViewer, ImageMarking -ICON_PATH = Path('images/icon.ico') -TOGGLE_MARKING_ICON_PATH = Path('images/toggle_marking.png') -SHOW_MARKINGS_ICON_PATH = Path('images/show_marking.png') -SHOW_LABELS_ICON_PATH = Path('images/show_label.png') -SHOW_MARKING_LATENT_ICON_PATH = Path('images/show_marking_latent.png') +from taggui.widgets.icons import (taggui_icon, create_add_box_icon, + toggle_marking_icon, show_markings_icon, + show_labels_icon, show_marking_latent_icon) + GITHUB_REPOSITORY_URL = 'https://github.com/jhc13/taggui' TOKENIZER_DIRECTORY_PATH = Path('clip-vit-base-patch32') @@ -64,7 +62,7 @@ def __init__(self, app: QApplication): self.tag_counter_model = TagCounterModel() self.image_tag_list_model = ImageTagListModel() - self.setWindowIcon(QIcon(QPixmap(get_resource_path(ICON_PATH)))) + self.setWindowIcon(taggui_icon()) # Not setting this results in some ugly colors. self.setPalette(self.app.style().standardPalette()) # The font size must be set before creating the widgets to ensure that @@ -114,25 +112,21 @@ def __init__(self, app: QApplication): 'Delete marking', self) self.delete_marking_action.setEnabled(False) self.toolbar.addAction(self.delete_marking_action) - self.add_toggle_marking_action = QAction( - QIcon(QPixmap(get_resource_path(TOGGLE_MARKING_ICON_PATH))), + self.add_toggle_marking_action = QAction(toggle_marking_icon(), 'Change marking type', self) self.add_toggle_marking_action.setEnabled(False) self.toolbar.addAction(self.add_toggle_marking_action) - self.add_show_marking_action = QAction( - QIcon(QPixmap(get_resource_path(SHOW_MARKINGS_ICON_PATH))), + self.add_show_marking_action = QAction(show_markings_icon(), 'Show markings', self) self.add_show_marking_action.setCheckable(True) self.add_show_marking_action.setChecked(True) self.toolbar.addAction(self.add_show_marking_action) - self.add_show_labels_action = QAction( - QIcon(QPixmap(get_resource_path(SHOW_LABELS_ICON_PATH))), + self.add_show_labels_action = QAction(show_labels_icon(), 'Show labels', self) self.add_show_labels_action.setCheckable(True) self.add_show_labels_action.setChecked(True) self.toolbar.addAction(self.add_show_labels_action) - self.add_show_marking_latent_action = QAction( - QIcon(QPixmap(get_resource_path(SHOW_MARKING_LATENT_ICON_PATH))), + self.add_show_marking_latent_action = QAction(show_marking_latent_icon(), 'Show marking in latent space', self) self.add_show_marking_latent_action.setCheckable(True) self.add_show_marking_latent_action.setChecked(True) @@ -157,7 +151,12 @@ def __init__(self, app: QApplication): self.image_list) self.addDockWidget(Qt.DockWidgetArea.RightDockWidgetArea, self.auto_captioner) + self.auto_markings = AutoMarkings(self.image_list_model, + self.image_list, self) + self.addDockWidget(Qt.DockWidgetArea.RightDockWidgetArea, + self.auto_markings) self.tabifyDockWidget(self.all_tags_editor, self.auto_captioner) + self.tabifyDockWidget(self.auto_captioner, self.auto_markings) self.all_tags_editor.raise_() # Set default widths for the dock widgets. # Temporarily set a size for the window so that the dock widgets can be @@ -183,6 +182,8 @@ def __init__(self, app: QApplication): self.toggle_all_tags_editor_action = QAction('All Tags', parent=self) self.toggle_auto_captioner_action = QAction('Auto-Captioner', parent=self) + self.toggle_auto_markings_action = QAction('Auto-Markings', + parent=self) self.create_menus() self.image_list_selection_model = (self.image_list.list_view @@ -194,6 +195,7 @@ def __init__(self, app: QApplication): self.connect_image_tags_editor_signals() self.connect_all_tags_editor_signals() self.connect_auto_captioner_signals() + self.connect_auto_markings_signals() # Forward any unhandled image changing key presses to the image list. key_press_forwarder = KeyPressForwarder( parent=self, target=self.image_list.list_view, @@ -458,6 +460,7 @@ def create_menus(self): self.toggle_image_tags_editor_action.setCheckable(True) self.toggle_all_tags_editor_action.setCheckable(True) self.toggle_auto_captioner_action.setCheckable(True) + self.toggle_auto_markings_action.setCheckable(True) self.toggle_toolbar_action.triggered.connect( lambda is_checked: self.toolbar.setVisible(is_checked)) self.toggle_image_list_action.triggered.connect( @@ -468,11 +471,14 @@ def create_menus(self): lambda is_checked: self.all_tags_editor.setVisible(is_checked)) self.toggle_auto_captioner_action.triggered.connect( lambda is_checked: self.auto_captioner.setVisible(is_checked)) + self.toggle_auto_markings_action.triggered.connect( + lambda is_checked: self.auto_markings.setVisible(is_checked)) view_menu.addAction(self.toggle_toolbar_action) view_menu.addAction(self.toggle_image_list_action) view_menu.addAction(self.toggle_image_tags_editor_action) view_menu.addAction(self.toggle_all_tags_editor_action) view_menu.addAction(self.toggle_auto_captioner_action) + view_menu.addAction(self.toggle_auto_markings_action) help_menu = menu_bar.addMenu('Help') open_github_repository_action = QAction('GitHub', parent=self) @@ -711,6 +717,14 @@ def connect_auto_captioner_signals(self): lambda: self.toggle_auto_captioner_action.setChecked( self.auto_captioner.isVisible())) + def connect_auto_markings_signals(self): + self.auto_markings.marking_generated.connect( + lambda image_index, markings: + self.image_list_model.add_image_markings(image_index, markings)) + self.auto_markings.visibilityChanged.connect( + lambda: self.toggle_auto_markings_action.setChecked( + self.auto_markings.isVisible())) + def restore(self): # Restore the window geometry and state. if settings.contains('geometry'): @@ -729,28 +743,3 @@ def restore(self): type=str)) if directory_path.is_dir(): self.load_directory(directory_path, select_index=image_index) - -def create_add_box_icon(color: QColor) -> QPixmap: - """Create a QPixmap for an icon""" - pixmap = QPixmap(32, 32) - pixmap.fill(QColor('transparent')) - - # Create a painter to draw on the pixmap - painter = QPainter(pixmap) - - # Draw a bordered rectangle in the specified color - rect = QRect(2, 2, 28, 28) - painter.setPen(QPen(color, 2)) - painter.drawRect(rect) - - # Draw a plus sign in the middle - painter.setPen(QPen(Qt.black, 1)) - path = QPainterPath() - path.moveTo(16, 10) - path.lineTo(16, 22) - path.moveTo(10, 16) - path.lineTo(22, 16) - painter.drawPath(path) - painter.end() - - return pixmap From ad7ccb5dc17e69e071e6c6271e10bcef0ffd0376 Mon Sep 17 00:00:00 2001 From: StableLlama Date: Tue, 25 Mar 2025 00:20:16 +0100 Subject: [PATCH 47/82] Make advanced YOLO settings available --- taggui/models/image_list_model.py | 1 - taggui/{widgets => utils}/icons.py | 0 taggui/widgets/auto_markings.py | 81 +++++++++++++++++++++++++----- taggui/widgets/main_window.py | 7 ++- 4 files changed, 71 insertions(+), 18 deletions(-) rename taggui/{widgets => utils}/icons.py (100%) diff --git a/taggui/models/image_list_model.py b/taggui/models/image_list_model.py index a470ce23..73deba86 100644 --- a/taggui/models/image_list_model.py +++ b/taggui/models/image_list_model.py @@ -251,7 +251,6 @@ def write_meta_to_disk(self, image: Image): meta: dict[str, any] = {'version': 1} if image.crop is not None: meta['crop'] = image.crop.getRect() - print('image.markings', image.markings) meta['markings'] = [{'label': marking.label, 'type': marking.type.name, 'confidence': marking.confidence, diff --git a/taggui/widgets/icons.py b/taggui/utils/icons.py similarity index 100% rename from taggui/widgets/icons.py rename to taggui/utils/icons.py diff --git a/taggui/widgets/auto_markings.py b/taggui/widgets/auto_markings.py index 464da1fd..a8726f69 100644 --- a/taggui/widgets/auto_markings.py +++ b/taggui/widgets/auto_markings.py @@ -2,23 +2,27 @@ from pathlib import Path from PySide6.QtCore import Signal, QModelIndex, Qt, Slot -from PySide6.QtGui import QTextCursor, QFont -from PySide6.QtWidgets import QDockWidget, QProgressBar, QPlainTextEdit, QWidget, QVBoxLayout, QScrollArea, \ - QAbstractScrollArea, QFrame, QFormLayout, QMessageBox, QTableWidget, QHeaderView, QTableWidgetItem, QComboBox +from PySide6.QtGui import QTextCursor +from PySide6.QtWidgets import (QDockWidget, QProgressBar, QPlainTextEdit, + QWidget, QVBoxLayout, QScrollArea, + QAbstractScrollArea, QFrame, QFormLayout, + QMessageBox, QTableWidget, QHeaderView, + QTableWidgetItem, QComboBox) +from utils.icons import create_add_box_icon from models.image_list_model import ImageListModel +from utils.utils import pluralize from utils.big_widgets import TallPushButton from utils.settings import settings, DEFAULT_SETTINGS -from utils.settings_widgets import FocusedScrollSettingsComboBox -from widgets.auto_captioner import set_text_edit_height, restore_stdout_and_stderr +from utils.settings_widgets import (FocusedScrollSettingsComboBox, + FocusedScrollSettingsDoubleSpinBox, + FocusedScrollSettingsSpinBox) +from widgets.auto_captioner import (set_text_edit_height, + restore_stdout_and_stderr, HorizontalLine) from widgets.image_list import ImageList from auto_marking.marking_thread import MarkingThread from dialogs.caption_multiple_images_dialog import CaptionMultipleImagesDialog -from widgets.icons import create_add_box_icon - -from taggui.utils.utils import pluralize - class MarkingSettingsForm(QVBoxLayout): model_selected = Signal(bool) @@ -44,7 +48,47 @@ def __init__(self): self.class_table.verticalHeader().setSectionResizeMode(QHeaderView.ResizeToContents) basic_settings_form.addRow('Classes', self.class_table) + self.toggle_advanced_settings_form_button = TallPushButton( + 'Show Advanced Settings') + + self.advanced_settings_form_container = QWidget() + advanced_settings_form = QFormLayout( + self.advanced_settings_form_container) + advanced_settings_form.setLabelAlignment(Qt.AlignmentFlag.AlignRight) + advanced_settings_form.setFieldGrowthPolicy( + QFormLayout.FieldGrowthPolicy.ExpandingFieldsGrow) + # Sets the minimum confidence threshold for detections. + # Objects detected with confidence below this threshold will be + # disregarded. Adjusting this value can help reduce false positives. + self.confidence_spin_box = FocusedScrollSettingsDoubleSpinBox( + key='confidence', default=0.25, minimum=0.01, maximum=1.0) + self.confidence_spin_box.setSingleStep(0.01) + advanced_settings_form.addRow('Confidence', + self.confidence_spin_box) + # Intersection Over Union (IoU) threshold for Non-Maximum Suppression + # (NMS). Lower values result in fewer detections by eliminating + # overlapping boxes, useful for reducing duplicates. + self.iou_spin_box = FocusedScrollSettingsDoubleSpinBox( + key='iou', default=0.7, minimum=0.01, maximum=1.0) + self.iou_spin_box.setSingleStep(0.01) + advanced_settings_form.addRow('Intersection Over Union (IoU)', + self.iou_spin_box) + # Maximum number of detections allowed per image. + # Limits the total number of objects the model can detect in a single + # inference, preventing excessive outputs in dense scenes. + self.max_det_spin_box = FocusedScrollSettingsSpinBox( + key='max_det', default=300, minimum=1, maximum=500) + advanced_settings_form.addRow('Maximum number of detections', self.max_det_spin_box) + self.advanced_settings_form_container.hide() + self.addLayout(basic_settings_form) + self.horizontal_line = HorizontalLine() + self.addWidget(self.horizontal_line) + self.addWidget(self.toggle_advanced_settings_form_button) + self.addWidget(self.advanced_settings_form_container) + + self.toggle_advanced_settings_form_button.clicked.connect( + self.toggle_advanced_settings_form) def get_local_model_paths(self): models_directory_path = settings.value( @@ -68,13 +112,23 @@ def get_local_model_paths(self): self.model_combo_box.addItem( str(path.relative_to(models_directory_path)), userData=path) + @Slot() + def toggle_advanced_settings_form(self): + if self.advanced_settings_form_container.isHidden(): + self.advanced_settings_form_container.show() + self.toggle_advanced_settings_form_button.setText( + 'Hide Advanced Settings') + else: + self.advanced_settings_form_container.hide() + self.toggle_advanced_settings_form_button.setText( + 'Show Advanced Settings') + def get_marking_settings(self) -> dict: return { - #'model_id': self.model_combo_box.currentText(), 'model_path': self.model_combo_box.currentData(), - 'conf': 0.25, - 'iou': 0.7, - 'max_det': 300, + 'conf': self.confidence_spin_box.value(), + 'iou': self.iou_spin_box.value(), + 'max_det': self.max_det_spin_box.value(), 'classes': [] } @@ -229,6 +283,7 @@ def generate_markings(self): if self.marking_thread is None: self.prepare_generation() self.marking_thread.selected_image_indices = selected_image_indices + self.marking_thread.marking_settings = self.marking_settings_form.get_marking_settings() classes = {} for row, (class_id, class_name) in enumerate( self.marking_thread.model.names.items()): diff --git a/taggui/widgets/main_window.py b/taggui/widgets/main_window.py index b3d6d335..85cb0d0f 100644 --- a/taggui/widgets/main_window.py +++ b/taggui/widgets/main_window.py @@ -17,6 +17,9 @@ from models.image_tag_list_model import ImageTagListModel from models.proxy_image_list_model import ProxyImageListModel from models.tag_counter_model import TagCounterModel +from utils.icons import (taggui_icon, create_add_box_icon, toggle_marking_icon, + show_markings_icon, show_labels_icon, + show_marking_latent_icon) from utils.big_widgets import BigPushButton from utils.image import Image from utils.key_press_forwarder import KeyPressForwarder @@ -30,10 +33,6 @@ from widgets.image_tags_editor import ImageTagsEditor from widgets.image_viewer import ImageViewer, ImageMarking -from taggui.widgets.icons import (taggui_icon, create_add_box_icon, - toggle_marking_icon, show_markings_icon, - show_labels_icon, show_marking_latent_icon) - GITHUB_REPOSITORY_URL = 'https://github.com/jhc13/taggui' TOKENIZER_DIRECTORY_PATH = Path('clip-vit-base-patch32') From eba1b58dc90fbe3279b4afa48e73e922b2c44d83 Mon Sep 17 00:00:00 2001 From: StableLlama Date: Tue, 25 Mar 2025 22:14:50 +0100 Subject: [PATCH 48/82] Add YOLO to the README.md --- README.md | 25 +++++++++++++++++++++++-- images/doc/cropping.jpg | Bin 294020 -> 850109 bytes 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index dbb05496..f86e395a 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,10 @@ like Stable Diffusion. - Batch tag operations for renaming, deleting, and sorting tags - Advanced image list filtering - Export images ready to be used for training +- Mark images manually or with the help of YOLO models to create masks for + inclusion and exclusion of image parts for masked training +- Crop images with advanced hints that respect relevant aspect ratios and + bucket sizes of the training scripts ## Installation @@ -272,7 +276,7 @@ they can be changed into each other. All markings are marking the pixels inside the border 🞑, not any pixels below the border. -![cropping.jpg](images/doc/cropping.jpg) +TagGUI cropping and masking feature ### Crop @@ -347,10 +351,27 @@ The marking label can be edited by clicking on it. And the type can be changed in the toolbar or with a right mouse button click on the marking. +### Automatic marking detection + +When you have the path to the YOLO models configured in the _Settings..._ +dialog, you can use them to automatically detect features in the image that +can be used for marking. After selecting the relevant model in the drop-down +list, you see a table with all classes it can detect. In each line you can +decide whether it should be ignored or added as a hint, exclude or include. +Next to the label you can see the confidence the model had during detection. + +More detailed control about the minimal required confidence, the IoU and +the maximal number of detected markings per image can be set in the +advanced settings. + +Note: When you are already using image generation tools like ADetailer you +will most likely have already relevant YOLO models. There are many models +widely available when you are searching for "YOLO" or "ADetailer". + ## Export Exporting the images to a directory allows different options. By choosing the -preset for the target AI model many important settings are automatically set. +preset for the target AI model, many important settings are automatically set. `Image selection`: Select whether all images, or those with the current filter or only the diff --git a/images/doc/cropping.jpg b/images/doc/cropping.jpg index 8bd00b14758d3e8146c183444978ed1d7fc078f0..7613dc6d1fa77e86b968eda4293d1d9e7ea3f26c 100644 GIT binary patch literal 850109 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%0AQ`5HIJyUCzeLi+vkF7Gv5cKqedGYdPvLSn=Y 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

%-j;w?$Z}^nmSZI#E0bZWJ5SZ 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

F3%*VNL@qM^6S?@(JVisqwVAfR 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

O7RY$PgbG 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

$df&;;EDq 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