Add image adjustment sliders, mask alpha, Load Prev Mask, and button state colours

- Three vertical sliders (Brightness, Contrast, Gamma) to the left of the
  canvas for display-only image adjustment; all use power/linear formulae
  applied on-the-fly without touching saved data
- Alpha slider controls mask overlay transparency
- Brush size slider moved to its own row
- Each slider has a reset (↺) button restoring its default value
- Hide Mask button turns red when active; Eraser button turns orange
- Load Prev Mask button copies the saved mask from the previous clip in
  the list onto the current clip; the action is pushed onto the undo stack
  so it can be reverted with Undo
- Right survey panel narrowed (stretch factor 2 → 1)
- README Controls section updated to document all new features

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-20 14:41:10 +02:00
parent d0f7cc64fc
commit d13ad1743a
3 changed files with 181 additions and 15 deletions

View File

@@ -4,7 +4,7 @@ from pathlib import Path
import cv2
import numpy as np
from PIL import Image
from PySide6.QtCore import QTimer
from PySide6.QtCore import Qt, QTimer
from PySide6.QtWidgets import (
QApplication,
QButtonGroup,
@@ -113,9 +113,10 @@ class Annotator(QMainWindow):
btn_undo = QPushButton("Undo")
btn_undo10 = QPushButton("Undo×10")
btn_redo = QPushButton("Redo")
btn_load_prev_mask = QPushButton("Load Prev Mask")
row1 = QHBoxLayout()
for b in [self.btn_prev, btn_next, btn_skip]:
for b in [self.btn_prev, btn_next, btn_skip, btn_load_prev_mask]:
row1.addWidget(b)
row2 = QHBoxLayout()
@@ -128,13 +129,42 @@ class Annotator(QMainWindow):
self.mc.btn_mask,
]:
row2.addWidget(b)
row2.addWidget(QLabel("Brush"))
row2.addWidget(self.mc.brush_slider)
row3 = QHBoxLayout()
row3.addWidget(QLabel("Brush size"))
row3.addWidget(self.mc.brush_slider)
row3.addWidget(self.mc.brush_reset)
row4 = QHBoxLayout()
row4.addWidget(QLabel("Alpha"))
row4.addWidget(self.mc.alpha_slider)
row4.addWidget(self.mc.alpha_reset)
vert_panel = QHBoxLayout()
vert_panel.setContentsMargins(0, 0, 4, 0)
for label_text, slider, reset_btn in [
("Brightness", self.mc.brightness_slider, self.mc.brightness_reset),
("Contrast", self.mc.contrast_slider, self.mc.contrast_reset),
("Gamma", self.mc.gamma_slider, self.mc.gamma_reset),
]:
col = QVBoxLayout()
lbl = QLabel(label_text)
lbl.setAlignment(Qt.AlignmentFlag.AlignHCenter)
col.addWidget(lbl)
col.addWidget(slider, 1)
col.addWidget(reset_btn)
vert_panel.addLayout(col)
canvas_row = QHBoxLayout()
canvas_row.addLayout(vert_panel)
canvas_row.addWidget(self.mc.canvas, 1)
left = QVBoxLayout()
left.addWidget(self.mc.canvas)
left.addLayout(canvas_row)
left.addLayout(row1)
left.addLayout(row2)
left.addLayout(row3)
left.addLayout(row4)
left_widget = QWidget()
left_widget.setLayout(left)
@@ -143,7 +173,7 @@ class Annotator(QMainWindow):
main = QHBoxLayout()
main.addWidget(left_widget, 3)
main.addWidget(right_widget, 2)
main.addWidget(right_widget, 1)
container = QWidget()
container.setLayout(main)
@@ -156,6 +186,7 @@ class Annotator(QMainWindow):
btn_undo.clicked.connect(self.mc.undo)
btn_undo10.clicked.connect(self.mc.undo10)
btn_redo.clicked.connect(self.mc.redo)
btn_load_prev_mask.clicked.connect(self.load_prev_mask)
if self._pending_answers:
self._set_answers(self._pending_answers)
@@ -339,3 +370,28 @@ class Annotator(QMainWindow):
def skip_clip(self):
self._advance_clip()
def load_prev_mask(self):
try:
idx = self.selector.clips.index(self.filename)
except ValueError:
return
if idx == 0:
QMessageBox.information(
self, "No previous clip", "This is the first clip in the list."
)
return
prev_clip = self.selector.clips[idx - 1]
mask_path = self.out_dir / prev_clip.stem / self.cfg.filenames.mask
if not mask_path.exists():
QMessageBox.information(
self, "No mask found", f"No saved mask found for '{prev_clip.stem}'."
)
return
mask_full = np.array(Image.open(mask_path).convert("L"))
mask = cv2.resize(
(mask_full > 127).astype(np.uint8),
(self.dw, self.dh),
interpolation=cv2.INTER_NEAREST,
)
self.mc.set_mask(mask)