Skip to content

Version 1.2#19

Merged
AlanRockefeller merged 2 commits intomainfrom
test
Nov 23, 2025
Merged

Version 1.2#19
AlanRockefeller merged 2 commits intomainfrom
test

Conversation

@AlanRockefeller
Copy link
Copy Markdown
Owner

@AlanRockefeller AlanRockefeller commented Nov 23, 2025

Improve auto white balance

Summary by CodeRabbit

Release Notes

  • New Features

    • Auto White Balance with configurable Lab and RGB modes, including strength, warm bias, and luminance/RGB boundary controls
    • Open folder dialog for streamlined directory navigation
    • Enhanced Settings panel with dedicated Auto White Balance controls
    • Improved UI theming with customizable dialog colors
  • Bug Fixes

    • Fixed documentation typo
    • Enhanced EXIF preservation for JPEG images
    • Improved image format detection
  • Improvements

    • Refined image scaling algorithm for optimized quality
    • Better error handling and logging throughout the application

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Nov 23, 2025

Walkthrough

The PR adds comprehensive Auto White Balance (AWB) functionality with Lab and RGB modes, introduces AWB configuration parameters, adds color-aware UI styling to multiple dialogs, modifies image saving to return path pairs for undo/redo tracking, and improves JPEG scaling with aspect-ratio awareness.

Changes

Cohort / File(s) Summary
Documentation & Metadata
ChangeLog.md, README.md, faststack.json
Added v1.2.0 changelog entry for AWB improvements; corrected typo ("high-peformance" → "high performance"); created new faststack.json config file.
Configuration
faststack/config.py
Added new "awb" section to DEFAULT_CONFIG with mode, strength, warm_bias, luma bounds, and rgb bounds parameters.
Core AWB Implementation
faststack/app.py
Introduced AWB dispatcher for Lab-based and Legacy RGB modes; added 15 new getter/setter methods for AWB parameters; implemented Lab-based and RGB AWB paths with masking, color space operations, and UI state updates; reworked clear_all_batches/clear_all_stacks semantics; added open_folder slot; modified undo history tracking for image edits; enhanced PIL.Image support for large images.
Image Processing
faststack/imaging/editor.py
Changed save_image return type from Optional[Path] to Optional[Tuple[Path, Path]]; refactored EXIF preservation logic with context manager; returns (saved_path, backup_path) tuple on success.
JPEG Decoding
faststack/imaging/jpeg.py
Updated decode_jpeg_resized scaling logic to be aspect-ratio aware; now determines constraining dimension based on image aspect relative to target box before computing scaling factor.
UI State & Signals
faststack/ui/provider.py
Added 7 new AWB-related signals and properties to UIState (awbMode, awbStrength, awbWarmBias, awbLumaLowerBound, awbLumaUpperBound, awbRgbLowerBound, awbRgbUpperBound) with getter/setter delegates to AppController; added open_folder slot.
QML Dialog Updates
faststack/qml/FilterDialog.qml, JumpToImageDialog.qml, ImageEditorDialog.qml
Added backgroundColor and textColor properties to each dialog; replaced hard-coded/theme-based colors with centralized color property bindings.
Settings Dialog
faststack/qml/SettingsDialog.qml
Added 7 new public properties for AWB controls; reorganized layout into multi-section column structure with Auto White Balance section; added conditional Advanced Settings block; implemented onAccepted assignments to propagate AWB values to uiState.
Main Application UI
faststack/qml/Main.qml
Introduced hoverColor derived from currentBackgroundColor; replaced hard-coded hover colors with dynamic hoverColor; switched popup/menu backgrounds to currentBackgroundColor; propagated backgroundColor and textColor to dialogs; extended settings flow to include AWB state; updated Open Folder action to call uiState.open_folder.
Removed
faststack/qml/ImageEditorDialog.qml.old
Deleted backup/legacy QML file (full Qt Quick window-based dialog implementation).

Sequence Diagram(s)

sequenceDiagram
    actor User
    participant QML as QML UI<br/>(SettingsDialog)
    participant UIState as UIState<br/>(provider.py)
    participant AppCtrl as AppController<br/>(app.py)
    participant Config as Config<br/>(config.py)

    User->>QML: Adjust AWB Mode/Strength
    QML->>UIState: Set awbMode/awbStrength
    UIState->>AppCtrl: get/set_awb_mode<br/>get/set_awb_strength
    AppCtrl->>AppCtrl: Update internal state
    AppCtrl->>Config: Read/persist AWB params
    AppCtrl-->>UIState: Return updated values
    UIState-->>QML: Signal change<br/>(awbModeChanged, etc.)
    QML->>QML: Update bindings
    Note over User,QML: Settings persisted in config
Loading
sequenceDiagram
    participant Editor as Image Editor<br/>(editor.py)
    participant ImageFile as Image File
    participant UndoMgr as Undo Manager<br/>(app.py)

    Editor->>Editor: Apply edits
    Editor->>ImageFile: save_image()
    ImageFile->>ImageFile: Backup original
    ImageFile->>ImageFile: Save modified image<br/>with EXIF (JPEG)
    ImageFile-->>Editor: (saved_path, backup_path)
    Editor->>UndoMgr: Track undo with<br/>(saved_path, backup_path)
    Note over Editor,UndoMgr: Previous: tracked only<br/>filepath tuple
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

  • AWB dispatcher and dual-path implementation (Lab vs. Legacy RGB) introduces substantial new logic with color space operations, masking, and state synchronization that requires careful validation.
  • save_image return type change (Optional[Path] → Optional[Tuple[Path, Path]]) represents an API-breaking change affecting undo/redo and all callers; backward compatibility impact must be verified.
  • JPEG scaling logic refactor alters core image decoding behavior; aspect-ratio calculation correctness is critical for image quality.
  • Repetitive but distributed UI changes: Multiple dialogs receive identical color property additions (backgroundColor, textColor), reducing per-file complexity but increasing total surface area.
  • QML state binding complexity: SettingsDialog layout reorganization and multi-way bindings between QML properties, UIState signals, and AppController slots require tracing signal flow.

Areas requiring extra attention:

  • Lab-based AWB computation (masking, clipping, OpenCV operations) and Legacy RGB channel-mean calculations for correctness
  • save_image EXIF handling fallback logic and tuple return compatibility with existing callers
  • JPEG decode_jpeg_resized aspect-ratio threshold logic for edge cases (square images, extreme aspect ratios)
  • QML color binding propagation chain (Main.qml → dialogs → text/background elements)
  • SettingsDialog onOpened/onAccepted logic for AWB property initialization and persistence

Possibly related PRs

  • Release v0.7 — more improvements #12: Modifies same UIState provider with additional signal/property additions (statusMessage, reset, color-space handling), indicating overlapping UI state expansion.
  • Version 0.8 #13: Extends UIState/controller surface for advanced color controls (colorMode, saturation properties and config keys), overlapping with AWB config/property additions.
  • Release v0.4 — more improvements #6: Modified decode_jpeg_resized in faststack/imaging/jpeg.py; this PR further alters the same scaling logic path.

Pre-merge checks and finishing touches

❌ Failed checks (1 inconclusive)
Check name Status Explanation Resolution
Title check ❓ Inconclusive The title 'Version 1.2' is vague and generic, referring to a version number rather than describing the actual changes. The PR's description and actual changes focus on improving auto white balance functionality, but the title provides no meaningful information about what was changed. Revise the title to be more descriptive, such as 'Improve auto white balance with Lab and RGB modes and advanced controls' to clearly convey the main changes to reviewers.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch test

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
faststack/faststack/app.py (1)

1753-1788: save_edited_image still assumes save_image returns a Path, not (saved_path, backup_path)

ImageEditor.save_image() now returns (saved_path, backup_path), but save_edited_image() still treats the result as a single Path. This will make original_path a tuple, so the later comparison if img_file.path == original_path will never match and the UI won’t navigate to the edited image correctly.

     @Slot()
     def save_edited_image(self):
         """Saves the edited image."""
-        saved_path = self.image_editor.save_image()
-        if saved_path:
+        save_result = self.image_editor.save_image()
+        if save_result:
+            saved_path, backup_path = save_result
             # Clear the image editor state so it will reload fresh next time
             self.image_editor.original_image = None
             self.image_editor.current_filepath = None
             self.image_editor._preview_image = None
@@
-            # Refresh the view - need to refresh image list since backup file was created
-            original_path = saved_path
+            # Refresh the view - need to refresh image list since backup file was created
+            original_path = saved_path

If you also want manual editor saves to be undoable like quick AWB, you could additionally push (saved_path, backup_path) into undo_history, but that’s an optional enhancement.

🧹 Nitpick comments (6)
faststack/ChangeLog.md (1)

8-8: Consider more specific wording.

The phrase "make it better" could be more descriptive. Consider something like "improved auto white balance algorithm" or "enhanced auto white balance with Lab and RGB modes".

faststack/README.md (1)

12-12: Consider hyphenating the compound adjective.

While the typo fix is good, the phrase "high performance decoding" would be more grammatically correct as "high-performance decoding" since it's a compound adjective modifying a noun.

faststack/faststack/imaging/editor.py (1)

245-300: save_image contract and error handling look reasonable; consider tightening exceptions/logging

The new (saved_path, backup_path) return and EXIF/format handling are consistent and should work for standard JPEG/bitmap formats. The nested try/except Exception with print() is a bit loose; if you care about diagnostics, consider catching narrower exceptions and routing through the logger instead of print, but it’s not blocking.

faststack/faststack/qml/SettingsDialog.qml (1)

27-32: AWB settings surface and UI wiring are coherent end‑to‑end

The new AWB properties on the dialog, their propagation to uiState in onAccepted, and the General/AWB tab layout (including advanced bounds) line up cleanly with the backend AWB getters/setters; I don’t see functional issues here. Only optional follow‑up would be persisting the “Advanced Settings” checkbox state if you want that UX.

Also applies to: 41-48, 50-66, 68-295

faststack/faststack/ui/provider.py (1)

87-93: UIState AWB properties and open_folder slot correctly mirror AppController

The new AWB properties cleanly delegate to app_controller getters/setters with matching notify signals, and open_folder is exposed as a simple slot wrapper. If slider-driven updates ever become noisy, you could add “value unchanged” checks in setters, but it’s not required.

Also applies to: 294-356, 446-449

faststack/faststack/app.py (1)

618-621: clear_all_batches now also clears stacks; align naming/docstrings with behavior

clear_all_batches() now delegates to clear_all_stacks(), and clear_all_stacks() was expanded to clear stacks, batches, and markers. If this unified “clear everything” behavior is intentional, consider renaming or updating the clear_all_batches docstring/UI copy so it doesn’t imply batches‑only.

Also applies to: 955-970

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f846dca and 09a0158.

📒 Files selected for processing (14)
  • faststack/ChangeLog.md (1 hunks)
  • faststack/README.md (1 hunks)
  • faststack/faststack/app.py (9 hunks)
  • faststack/faststack/config.py (1 hunks)
  • faststack/faststack/faststack.json (1 hunks)
  • faststack/faststack/imaging/editor.py (2 hunks)
  • faststack/faststack/imaging/jpeg.py (1 hunks)
  • faststack/faststack/qml/FilterDialog.qml (4 hunks)
  • faststack/faststack/qml/ImageEditorDialog.qml (9 hunks)
  • faststack/faststack/qml/ImageEditorDialog.qml.old (0 hunks)
  • faststack/faststack/qml/JumpToImageDialog.qml (3 hunks)
  • faststack/faststack/qml/Main.qml (13 hunks)
  • faststack/faststack/qml/SettingsDialog.qml (2 hunks)
  • faststack/faststack/ui/provider.py (3 hunks)
💤 Files with no reviewable changes (1)
  • faststack/faststack/qml/ImageEditorDialog.qml.old
🧰 Additional context used
🧬 Code graph analysis (2)
faststack/faststack/ui/provider.py (1)
faststack/faststack/app.py (15)
  • get_awb_mode (1097-1098)
  • set_awb_mode (1101-1103)
  • get_awb_strength (1106-1107)
  • set_awb_strength (1110-1112)
  • get_awb_warm_bias (1115-1116)
  • set_awb_warm_bias (1119-1121)
  • get_awb_luma_lower_bound (1124-1125)
  • set_awb_luma_lower_bound (1128-1130)
  • get_awb_luma_upper_bound (1133-1134)
  • set_awb_luma_upper_bound (1137-1139)
  • get_awb_rgb_lower_bound (1142-1143)
  • set_awb_rgb_lower_bound (1146-1148)
  • get_awb_rgb_upper_bound (1151-1152)
  • set_awb_rgb_upper_bound (1155-1157)
  • open_folder (1174-1179)
faststack/faststack/app.py (4)
faststack/faststack/ui/provider.py (9)
  • clear_all_stacks (377-378)
  • get_default_directory (435-436)
  • set_default_directory (439-440)
  • open_directory_dialog (443-444)
  • open_folder (447-448)
  • white_balance_by (552-553)
  • white_balance_by (556-557)
  • white_balance_mg (560-561)
  • white_balance_mg (564-565)
faststack/faststack/config.py (5)
  • save (67-75)
  • get (77-78)
  • getfloat (83-84)
  • getint (80-81)
  • load (48-64)
faststack/faststack/io/sidecar.py (2)
  • save (62-91)
  • load (28-60)
faststack/faststack/imaging/editor.py (2)
  • save_image (245-302)
  • set_edit_param (234-239)
🪛 LanguageTool
faststack/ChangeLog.md

[style] ~8-~8: The wording of this phrase can be improved.
Context: ...k cool. - Updated auto white balance to make it better, and put some controls for it in the se...

(MAKE_STYLE_BETTER)

faststack/README.md

[grammar] ~12-~12: Use a hyphen to join words.
Context: ...10ms next/previous image switching, high performance decoding via PyTurboJPEG. ...

(QB_NEW_EN_HYPHEN)

🪛 Ruff (0.14.5)
faststack/faststack/imaging/editor.py

294-294: Do not catch blind exception: Exception

(BLE001)


299-299: Consider moving this statement to an else block

(TRY300)

faststack/faststack/app.py

1159-1159: Redefinition of unused get_default_directory from line 1093

(F811)


1889-1889: Use logging.exception instead of logging.error

Replace with exception

(TRY400)


1940-1940: Use logging.exception instead of logging.error

Replace with exception

(TRY400)

🔇 Additional comments (13)
faststack/faststack/qml/JumpToImageDialog.qml (1)

15-24: LGTM! Color theming implemented correctly.

The placeholder colors provide good visual feedback if the properties aren't properly initialized from Main.qml, and the implementation correctly replaces Material theme-based styling with explicit color properties.

faststack/faststack/faststack.json (1)

1-6: LGTM! Valid initialization structure.

The JSON structure is valid and appropriate for persistent storage of application state with empty initial collections.

faststack/faststack/config.py (1)

31-39: LGTM! Well-structured AWB configuration.

The new AWB configuration section follows the established pattern and includes reasonable defaults. The inline comment documents the mode options clearly.

faststack/faststack/imaging/jpeg.py (1)

122-130: LGTM! Correct aspect-ratio aware scaling.

The cross-product comparison correctly determines which dimension constrains the resize operation by comparing the image aspect ratio to the target aspect ratio. This is an improvement over the previous max(width, height) approach and will produce more optimal TurboJPEG scaling factors.

faststack/faststack/qml/FilterDialog.qml (1)

15-24: LGTM! Consistent color theming implementation.

The color properties are applied consistently across all UI elements, following the same pattern as the other dialogs in this PR.

faststack/faststack/qml/ImageEditorDialog.qml (2)

15-16: LGTM! Color theming implemented consistently.

The backgroundColor and textColor properties are correctly applied throughout the dialog, maintaining consistency with the other dialogs in this PR.

Also applies to: 41-41


218-218: LGTM! Dynamic slider track color provides proper contrast.

The conditional use of Qt.lighter vs Qt.darker based on the background color ensures the slider track remains visible regardless of the theme.

faststack/faststack/qml/Main.qml (1)

25-28: Centralized theming, hover color, and open_folder wiring look solid

Using currentBackgroundColor/hoverColor across menus, footer, and dialogs keeps light/dark theming consistent, and the uiState.open_folder() hookup for “Open Folder…” is correctly guarded on uiState. Just make sure FilterDialog, JumpToImageDialog, and ImageEditorDialog all declare backgroundColor/textColor properties as expected by these bindings.

Also applies to: 100-105, 127-132, 154-159, 181-186, 215-238, 302-311, 424-428, 563-567, 628-635, 850-870

faststack/faststack/app.py (5)

32-34: Disabling Pillow’s MAX_IMAGE_PIXELS is a conscious DoS trade‑off

Setting Image.MAX_IMAGE_PIXELS = None removes Pillow’s decompression‑bomb protection, allowing arbitrarily large images. That’s fine for trusted local image sets, but if there’s any chance of untrusted input, you may want to cap this or at least document the risk.


955-970: clear_all_stacks implementation is coherent for stacks + batches

Within its new scope, clear_all_stacks() correctly resets stack/batch lists and start markers, updates the sidecar, invalidates cached metadata, and syncs UI state with a status message. No functional issues seen here.


1806-1861: quick_auto_white_balance: tuple handling and undo wiring look correct

quick_auto_white_balance() correctly unpacks (saved_path, backup_path) from save_image() and records both paths in undo_history for the auto_white_balance action. The subsequent cache invalidation and UI refresh are consistent with the general image‑change pattern.


1855-1885: auto_white_balance dispatcher and legacy (RGB) implementation are logically sound

The dispatcher selects between auto_white_balance_lab() and auto_white_balance_legacy() based on config['awb']['mode'], with a clear error path for unknown modes. The legacy RGB “grey world” implementation clamps slider values and updates both the editor and UI state consistently.


1927-2015: Lab‑based AWB implementation is consistent with slider semantics and config parameters

The Lab AWB path:

  • Filters pixels by RGB and luma bounds from config['awb'],
  • Computes mean a*/b* on the masked set,
  • Targets (128, 128+warm_bias) and scales shifts into white_balance_by/white_balance_mg using strength and a fixed factor,
  • Updates both editor and UI state, then refreshes the image.

This matches the white-balance math in ImageEditor._apply_edits and respects the new AWB config fields.

Comment thread faststack/faststack/app.py
Comment thread faststack/faststack/app.py
@AlanRockefeller AlanRockefeller merged commit 05b2a47 into main Nov 23, 2025
1 check passed
@coderabbitai coderabbitai Bot mentioned this pull request Nov 23, 2025
Merged
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant