Skip to content

Latest commit

 

History

History
718 lines (660 loc) · 53.5 KB

CHANGELOG.md

File metadata and controls

718 lines (660 loc) · 53.5 KB

Version 2.1.0 - (2024-xx-xx)

This update includes dependency changes - Please run the command "pip install -r requirements.txt" or equivalent after downloading.

TODO: Configuration Tab

TODO: GIF Quality Slider

TODO: Investigate image to memory for checking file size estimate (low priority)

Added

  • Save Morph Tab
    • Save feature from v0.2.8 has been overhauled into its own dedicated tab for more refined control of PIM's output
      • Output Format Toggle (Single vs Multi-Frame)
      • GIF + Frame Dump Modes for Multi-Frame
        • GIF Settings: Loop, Rewind, Reverse, Frame Time
      • Image Extension Selector (.jpg, .png) for Single and Multi-Frame Dump
      • Configurable Default Output Location
      • Unreleased: GIF Quality Slider
        • Comment: This release does contain the UI placeholder, but the feature itself needs more work. Widget is disabled for now.
    • Special thanks to GitHub user NoJustAnother for creating this feature request, which pushed me to properly improve this system within PIM's GUI

Changes

  • Applied anti-aliasing to points / triangles rendered within the GUI
  • Progress bar used for Full Blends now has animations for being shown, updated, and hidden
  • Cursor Mode (Create, Move, Delete) is now additionally indicated in the bottom right corner for clarity
  • Readability: Changed 'Full Blend' (0.25 to 0.001) → 'Frame Count' (3 to 1000)
  • Improved error handling in checkUpdate() to catch other cases - e.g. when the user's Internet connection is down
    • PIM skips the update check step if any exception is encountered
  • Slightly restructured GUI's MainWindow footer due to save widget migration
  • Updated various tooltips
  • Updated Help section
  • Updated README.md

Fixes

  • Modified requirements.txt:
    • Replaced opencv-python with opencv-python-headless to resolve plugin issues between Qt and OpenCV

Known Bugs

  • While zoomed in, issue with highlighting points to be manually moved or deleted
  • After clicking Resize Left/Right, points on the GUI do not visibly scale correctly
    • Comment: This is a purely visual bug. For now, this issue can be circumvented by just reloading the affected image.

Version 2.0.2 - (2021-12-29)

This update includes dependency changes - Please run the command "pip install -r requirements.txt" or equivalent after downloading.

Added

  • Configuration Tab (Work in Progress)
    • Initial GUI implementation for saving, loading, and handling default parameters requested by the user, such as default image search path
    • All widgets are currently disabled and will be enabled in a future update when ready
      • Comment: Releasing the current implementation for this tab as-is in hopes of receiving feedback on the interface before full release.

Changes

  • Image loading methods loadDataLeft() and loadDataRight() now default to the Images_Points folder when the user is prompted to select an image
    • This will be configurable when the Configuration tab is fully implemented and released
  • Code cleanup: Merged and removed duplicate methods for various GUI tasks to reduce unnecessary bloat - more planned in the future
    • updateRed() / updateGreen() / updateBlue()updateColorSlider
    • triangleRedValueDone() / triangleGreenValueDone() / triangleBlueValueDone() → Removed in favor of directly calling verifyValue
  • Rewrote checkUpdate() to instead parse api.github.com as well as silently terminate on exception or failure
  • Modified requirements.txt:
    • Added Requests - missing from 2.0 release, required for checkUpdate()
    • Removed BeautifulSoup - no longer needed due to checkUpdate() changes
  • Miscellaneous: Updated comments for initialized GUI variables and signal connections

Version 2.0.1 - (2021-08-15)

Fixes

  • Resolved crash when the total point pair count was brought below three through Delete Mode
  • Corrected issue with Delete Mode that caused points to be improperly deleted (eventually cascaded into crashes)
  • Fixed bug with image loading that caused PIM to crash when only one point pair was previously saved
  • Removed unnecessary print statement on startup

Version 2.0.0 - (2021-07-28)

As no issues have been reported with 2.0.0 Beta, it is now being released as Stable with a couple additions.

This update includes new dependencies - Please run the command "pip install -r requirements.txt" or equivalent after downloading.

Added

  • New Mouse Modes
    • In addition to point placement, the user can now switch between two other modes when clicking on input images: Move Mode and Delete Mode
      • (E) - Move Mode allows the user to drag any previously confirmed point to a new location
      • (Q) - Delete Mode allows the user to delete any previously confirmed point
      • Special thanks to GitHub user jankaWIS for creating this feature request, which got me to finally add these functionalities to PIM's GUI
  • Dynamic Image Loading
    • The user can now load images into the GUI from their OS by dragging and dropping them into the desired window
    • Input images are now dynamically resized to match the size of their windows in the GUI, greatly improving GUI responsiveness
      • Larger images (e.g. 1080p, 4K, etc.) are downscaled, smaller images are upscaled
      • The user's files themselves are unchanged - PIM instead manipulates copies that get cleaned up during termination
      • Comment: Implementing this was - by far - the most painful thing I have experienced with software development as this ended up impacting pretty much every single part of PIM and it's code... which is also why this release was delayed for so long. I expect that any hotfixes needed for this update will arise from this unexpectedly complicated change, but I will be more than happy if that isn't the case (please work for others besides me...).
  • Zoom Panning
    • The user can now pan images while zoomed in by clicking and dragging with the middle-mouse button
  • Zoom Slider
    • QoL: The user can now manually set the strength of the zoom applied to GUI images (in realtime) for ease of use
      • Zoom strength is defaulted to 2x during initialization but can now range from 2x to 10x!
  • Automatic Versioning Checker
    • On startup, PIM now checks your currently installed version against the latest stable release hosted on GitHub
    • A prompt is provided to automatically navigate the user to the latest release, if not up to date
      • PIM does not support automatic update installation at this time
  • Triangle Widget Enhancements
    • In addition to the sliders, the user can now manually enter RGB values for the displayed triangles
      • The user can now choose between binary, decimal, or hexadecimal format when setting RGB values
  • More Macros
    • The following key combinations have been added to support the user's interactions with the GUI:
      • D = Toggle Display of Delaunay Triangles
      • E = Toggle Move Mode (for moving individual points)
      • Q = Toggle Delete Mode (for deleting individual points)
      • Ctrl + Mouse Wheel = Adjust Zoom Slider
        • Comment: Yes, this can be used with zoom panning simultaneously, so go nuts ;)
      • Shift + Mouse Wheel = Adjust Point Slider
      • Alt + Mouse Wheel = Adjust Alpha Slider

Changes

  • PIM's GUI has received many needed internal changes. The following elements have been changed:
    • Redesign:
      • The GUI has been restructured from scratch to containerize separate blocks
        • As this allows for far more refined control over resizing behavior - which has historically been a very problematic piece of development for this project - EVERY documented resizing bug has been fixed with this change (more details in Fixes)
    • Miscellaneous:
      • Minimum size of the main window has changed from (788 x 690) to (844 x 763)
        • Comment: Still trying to find that sweet spot for the GUI as it is refined and enhanced over time.
  • Optimization: MorphingApp.py no longer conditionally sets minimum/maximum/fixed size rules for the image windows (as Qt handles resizing correctly now)
  • Complete rewrite of blendImages() to support proper QThreading during morphing, preventing GUI lockup
    • To accompany this change, all functions related to morphing are disabled during the process to prevent exploits
    • Comment: Trading some performance for stability here since GUI threading during long calculations is good practice anyways. This opened the door to a proper progress bar as well, since that signal can now be emitted.
  • Implemented a proper progress bar for full blending - the notification bar is no longer used for this
  • Full blending now displays frames as they are rendered
    • PIM will still display the user's specified frame when finished morphing
  • Alpha is now assigned 50% instead of 0% when reset
  • PIM now prompts for confirmation when the Reset Points button is pressed
  • Added queue & warnings to MorphingApp.py's imports
  • Removed Matplotlib and itertools from Morphing.py's imports
    • As it was a dependency that is no longer in use, removed Matplotlib from requirements.txt as well
    • Comment: This additionally translates to a small performance improvement during Morphing.
  • Added pynput, beautifulsoup, & opencv-python to requirements.txt
    • Comment: The addition of pynput is to accommodate changes to resizing with this update due to Qt being unable to detect kernel level interrupts from mouse events in the running OS. This change shouldn't introduce any OS incompatibilities, but please create an issue if resizing the GUI leads to any unexpected issues or crashes. BeautifulSoup allows PIM to check for updates online. Opencv-python has finally been included for it's superior image reading/saving compared to Pillow & Imageio (both of which are planned to be phased out in a future update).
  • Added version.txt to facilitate automatic versioning checks.
    • Comment: Modifying, deleting, or moving this file will confuse PIM and ruin its day, but should not cause issues for you.
  • Removed NumPy's version restriction in requirements.txt (as the fmod() issue in v1.19.4 has been resolved)
  • Simplified all remaining instances of legacy code where the last index of a list was being accessed
    • x[len(x)-1] → x[-1]
  • Removed an instance of self.repaint() inside MorphingApp.py's checkResize()
  • Removed multiple instances of self.refreshPaint() inside MorphingApp.py's keyPressEvent()
  • Updated README.md
  • And much, much more

Fixes

  • Every single documented resizing bug has been hit with this update's GUI changes, including but not limited to:
    • Fixed: Input images would expand (or rarely shrink) by 2 pixels when loaded or each time the GUI was resized (very common)
    • Fixed: The bottom half of the GUI would often vertically expand/collapse before the top half (very common)
    • Fixed: The left half of the GUI would often horizontally expand/collapse before the right half (uncommon)
    • Fixed: Zooming into input images would cause random, incorrect resizing behavior (common)
    • Fixed: When maximized, the GUI would resize incorrectly with conditionally varying severity (very common)
    • Fixed: Inconsistent resizing behavior before and after a morph had been executed (very common)
  • Fixed a long-standing bug where the GUI could temporarily become unresponsive while morphing
    • Additionally, fixed a bug where inputs to the GUI would be propagated during morphing
      • Example: This could lead to chain-morphing if the blend button was clicked more than once
  • Fixed a bug where PIM could treat certain incompatible images as standard .jpg, causing a crash during morphing
  • Fixed a bug where zooming in on the lower third of either input image would be visibly distorted
  • Prioritized GUI parameter assignment to come first in the loadImage functions - thereby resolving several bugs & crashes
  • Fixed an amusing bug where the Help tab could be edited by the user
  • Fixed a crash that could occur upon resetting the alpha slider after executing a full blend
  • Resolved a minor GUI issue where, when enabled, Green/Blue triangle color value text would remain grey until updated
  • Fixed a bug where the Add Corners button would sometimes not enable after clicking Resize Left Image or Resize Right Image
  • Corrected a notification bar bug when one image has been loaded and user tries to add point
  • Other general code cleanup

Known Bugs

  • While zoomed in, issue with highlighting points to be manually moved or deleted
  • After clicking Resize Left/Right, points on the GUI do not visibly scale correctly
    • Comment: This is a purely visual bug. For now, this issue can be circumvented by just reloading the affected image.

Unreleased

  • Key Macro: Tab / Shift + Tab = Switch Morphing Tab
  • Configuration Tab
    • Comment: In the future, this tab will be used to set/reload default parameters for PIM to use on initialization. Still planning out what I do and don't want to be included in this tab though, so it has been excluded from this release.

Version 1.1.2 - (2021-05-02)

Hotfix:

  • Fixed a bug where the absolute path to Images_Points was incorrectly assigned for non-Windows machines
    • '\\' → os.path.sep

Version 1.1.1 - (2021-04-29)

Hotfix:

  • Fixed a bug where starting/ending image names could be incorrectly assigned when certain OS conditions were met
    • re.search('(?<=[/])[^/]+(?=[.])', x).group() → os.path.splitext(os.path.basename(x))

Version 1.1.0 - (2021-01-16)

Known Bugs

  • The GUI can sometimes become unresponsive during morphing calculations (but eventually returns to normal)
    • QtCore.QCoreApplication.processEvents() is a potential workaround but currently produces buggy results

Added

  • Point Size Slider
    • QoL: The user can now manually change the size of points rendered onto the GUI (in realtime) for ease of use
    • Point width values still default to 4 during initialization but can now range from 1 to 10

Changes

  • PIM's GUI has received another facelift with this update! The following elements have been changed:
    • Redesign:
      • Along with a streamlined layout, buttons & settings have been moved to categorized tabs
    • Accessibility:
      • PIM now includes a Help manual for new users! Check the Help tab in the GUI for more.
      • Minimum size of the main window has changed from (878 x 809) to (788 x 690)
        • Comment: I realize that there are still people in the world using 720p monitors.. this one's for you.
    • Miscellaneous:
      • Added dedicated value boxes for each of the triangle color sliders
        • Changes in value will no longer be announced in the notification bar
      • Alpha is now initialized at 50% instead of 0%
        • Comment: Honestly, not really sure why this wasn't always the case.
  • Updated README.md

Fixes

  • Fixed an ugly bug where zooming in to only one image could reshape the GUI
  • Fixed a notification bar bug regarding mouse clicks when only one image was loaded

Version 1.0.0 - Finally! - (2020-12-13)

Known Bugs

  • The GUI can sometimes become unresponsive during morphing calculations (but eventually returns to normal)
    • QtCore.QCoreApplication.processEvents() is a potential workaround but currently produces buggy results

Added

  • Image Zoom - Because sometimes, it's hard to get that one point just right.
    • The user can now right-click on either [or both] of the input images to toggle zoom for more accurate point placement
      • Comment: Sheesh, this was difficult to implement correctly with Qt. Currently this feature is using a default (and moderate) zoom strength of 2x, but this may be subject to change in the future - might use 2.5x or 3x if it seems that 2x isn't cutting it.

Removed

  • As of v0.3.0.1's hot pixel fix, PIM's image smoothing feature is deprecated and will now be removed
    • Removed Morphing.py's smoothBlend() method as well as the "smoothMode" parameter in getImageAtAlpha()
    • Removed Morphing.py's sub-module import for SciPy's median_filter
    • Removed all code related to smoothing in MorphingApp.py (a reduction of 77 SLOC)
    • Removed self.smoothingBox from MorphingGUI.ui and MorphingGUI.py
      • Comment: It's likely that this checkbox will be replaced with an automatic correspondence button at some point.

Changes

  • Improved morphing performance (a huge 90% speedup) by modifying Morphing.py's implementation of getPoints() as well as tweaking interpolatePoints() to utilize RectBivariateSpline's .ev() method instead of manually interpolating the image data
    • Huge thanks to GitHub user zhifeichen097 for his source code which can be found here - excellent work!
  • Optimized the conditional logic found in MorphingApp.py's displayTriangles()
  • Optimized point assignment in Morphing.py's loadTriangles() by utilizing np.loadtxt()
  • Optimized conditional logic and list pop statements in MorphingApp.py's keyPressEvent()
  • Changed the loop in MorphingApp.py's autoCorner() to be less C-like and more Pythonic
    • "i = 0; while i < 4: ... i++" → "for leftPoint, rightPoint in zip(tempLeft, tempRight): ..."
  • Moved autoCorner()'s invocation of refreshPaint() out of it's loop (i.e. the GUI is now updated once instead of up to four times)
  • Changed the notification message displayed when autoCorner() adds one point pair
  • Removed a conditional in MorphingApp.py's resizeLeft() and resizeRight() that was unnecessarily reassigning their image type variable
  • Converted the syntax of all instances where lists were being reset in MorphingApp.py
    • "self.blendList = []" → "self.blendList.clear()"
  • Updated a couple source code comments that became deprecated due to recent updates (oops)

Fixes

  • Resolved an oversight where .jpeg images couldn't be loaded into the program
    • Comment: To clarify, while it can probably accept other types, PIM is specifically written to work with .jpg, .jpeg, and .png images.
  • Corrected unintended behavior in mousePressEvent() where points could also be drawn with the middle and right mouse buttons

Version 0.3.0.1 - (2020-12-01)

Fixes

  • Fixed a long-standing bug where hot pixels frequently appeared in blended images [before and after]
    • Comment: While researching 2D interpolation methods in Python for what feels like the hundredth time (and still concluding that RectBivariateSpline is apparently the best method I can use), I noticed the optional 'kx' and 'ky' parameters that this method supports. Naturally, the documentation on them isn't very helpful (and honestly, I'm too lazy to put my computer engineering hat back on to understand what Wikipedia makes of them), but it does mention that the default value for each parameter is 3 degrees.. which got me thinking that a change here could lead to a potential performance improvement for PIM! In an unexpected turn of events, from what I can tell, setting 'kx' and 'ky' to 1 completely removes ALL hot/dead pixels that were appearing during interpolation. As a bonus, it also gives a 1-2% performance improvement based on surface level analysis.. win-win.

Version 0.3.0.0 - (2020-11-21)

Known Bugs

  • The GUI can sometimes become unresponsive during morphing calculations (but eventually returns to normal)
    • QtCore.QCoreApplication.processEvents() is a potential workaround but currently produces buggy results

Unreleased

  • Image Zoom
    • The user may right click on an image to zoom in and out of it to place more accurate points
    • Comment: Currently, image zoom functionality has been (somewhat) implemented but point placement will require additional changes.

Added

  • Freestyle Point Placement - Gone are the days when point pairs had to begin with the left image!
    • QoL: The user can now place point pairs on the images in whatever order they wish
    • Keyboard/mouse input logic has been rewritten to maintain previous behavior with Undo, Delete, OUT, etc.
  • Redo (CTRL+Y) Functionality
    • The user may now redo any point placement that was previously undone or deleted
      • Points are recovered in the order they were undone or deleted
      • The cache is cleared whenever a new point is placed by the user

Changes

  • Added logging that was previously missing from MorphingApp.py's Undo logic
  • Polished the string comparator found in MorphingApp.py's gifTextDone()
  • Optimized out ~34 SLOC from the Undo logic found in MorphingApp.py's keyPressEvent()
  • Optimized out ~25 SLOC from MorphingApp.py's displayTriangles() where Morphing.py's loadTriangles() should have been used
  • Triangle display behavior during MorphingApp.py's mousePressEvent() has been further consolidated and streamlined
  • To accommodate this update, all instances of self.currentWindow have been removed
    • Since much of mousePressEvent() and keyPressEvent() depended on this variable, these methods have been changed accordingly
    • Comment: The 'current window' variable was unintuitive legacy code written to get a working prototype out and, therefore, has not been very maintainable. This update replaces it with a much more useful self.clicked_window_history, it's spiritual successor!
  • All instances of self.persistFlag have been removed
    • Comment: More unintuitive legacy code. Since this variable is no longer used - and to improve maintainability - it is being removed.
  • All instances of self.confirmed_left_points_history and self.confirmed_right_points_history have been removed
    • Comment: These were placeholders for prototyping redo functionality a while back - this is now handled by self.placed_points_history.

Fixes

  • Fixed a bug where the alpha slider and auto-corner button were not enabling when new images were loaded
    • Comment: Just an oversight that spawned from the changes to the image loading methods in v0.2.9.0

Version 0.2.9.0 - (2020-09-05)

Note: Since I am taking up a software developer role next week, releases are going to slow down for the foreseeable future.

Known Bugs

  • The GUI can sometimes become unresponsive during morphing calculations (but eventually returns to normal)
    • QtCore.QCoreApplication.processEvents() is a potential workaround but currently produces buggy results

Added

  • Source Image Resizing
    • Since PIM requires the user's images to be the same size, it can now create and hotswap resized copies of the left/right images at the click of a button
    • For simplicity, one button resizes the left image to the right image's dimensions and the other button does the opposite
      • Both images must be loaded to enable this functionality
      • Additionally scales any added/confirmed/chosen points to the new image dimensions
      • Buttons will be set to bold whenever the user's images aren't the same size
    • Resized image files are stored under: ROOT_DIR/Images_Points/filename-[width]x[height].filetype
    • Resized text files are stored under: ROOT_DIR/Images_Points/filename-[width]x[height]-filetype.txt

Changes

  • The program now locks out usage of point placement as well as the Add Corners button when the user's images aren't the same size
    • Comment: This is to really drive the point home: fix one of your images or you'll just be wasting your time!
  • Moderate rewrite of loadDataLeft() and loadDataRight()
    • New helper function: checkFiles(sourceFunc, basePath, rootPath)
    • Text files are now stored under: ROOT_DIR/Images_Points/filename-filetype.txt
    • Comment: While polishing these, I found that due to how the functions were written, it was possible for the program to generate text files with varying naming schemes.. some ended with 'filename.txt', others ended with 'filename.FILETYPE.txt'. This has been streamlined into a more readable 'filename-filetype.txt' - images that share a name but differ in type should play more nicely with this change.

Version 0.2.8.2 - (2020-08-26)

Changes

  • Support for morphing two images of different sizes, while initially left up in the air as a potential feature, is no longer being considered
    • Consequently, the variables self.leftScalar and self.rightScalar have been converted to self.imageScalar
      • Since only one scalar is calculated now, resizeEvent() has received a performance boost as a result
    • The program will now warn the user (via the notification bar) when they load two images of different sizes
      • Comment: I'm considering adding two buttons to the GUI that resize the left image to the right image and vice versa.
  • Memory Optimization: Removed all instances of the variables used to:
    • Flag when the GUI was being resized: self.resizeFlag
    • Flag when left/right images were loaded: self.leftOn, self.rightOn
    • Flag whether left/right images were PNGs: self.leftPNG, self.rightPNG, leftTypeRegex, rightTypeRegex
    • Flag whether the smoothing box was checked: self.smoothBoxSetting
    • Flag whether the transparency box was checked: self.transparencySetting
    • Flag whether the full blend box was checked: self.blendBoxSetting
    • Flag whether the user has performed a morph in the current session: self.blendExecuted
    • Store triangle color slider values: self.redVal, self.blueVal, self.greenVal
    • Store an ADDITIONAL COPY (!!!) of each image's chosen points: self.startingText, self.endingText
      • Comment: This was legacy code, for sure, but it took me a bit to even understand that these LISTS (not boolean flags.. lists!) existed purely to indicate whether each input image's text file was empty or not. The memory cost for implementing that check this way was laughable, so the program now uses "if not os.stat(filename).st_size:" instead. Much better!
  • Variables declared during MorphingApp.py's initialization have been re-arranged by type (for source code clarity)
    • Additionally, detailed comments have now been given to all variables (and Qt signals) found under initialization (for source code clarity)

Fixes

  • Fixed a long-standing bug where .jpg blends could sometimes appear in the GUI to be wildly distorted and - occasionally - grayscale
    • Comment: When saved, these images were perfectly fine.. the problem was coming from a Qt pixmap conversion error. A BPL (bytes per line) parameter has been added to .jpg QImage construction syntax to resolve the issue.
  • autoCorner() now checks that neither image contains each corner point before adding it
    • Comment: Originally, the function was only checking that the left image didn't have each particular corner point; in retrospect, this could potentially cause problems if the right image DID have that corner point (as this would then lead to duplication for the right image).
  • Removed a conflicting legacy initialization of self.leftSize and self.rightSize in MorphingApp.py

Version 0.2.8.1 - (2020-08-25)

Fixes

  • Hotfixed a bug where verifyValue() for the frame time textbox was accepting 0 as a valid number

Version 0.2.8.0 - (2020-08-24)

Known Bugs

  • The GUI can sometimes become unresponsive during morphing calculations
    • QtCore.QCoreApplication.processEvents() is a potential workaround but currently produces buggy results

Added

  • Morphed Image Saving
    • Normal blends can now be saved as their respective image types; full blends can now be saved as dynamic GIFs
      • Generated GIFs are subject to the user's preference for the amount of time (default: 100 ms) given to each frame

Changes

  • To accommodate this update, the following elements in MorphingGUI.ui and MorphingGUI.py have been changed:
    • Added a "Save Image(s)" button alongside a smaller frame time textbox
      • A fairly air-tight Qt mask is used to restrict letter and symbol input - format is "[0-9][0-9][0-9] ms"
      • The program handles other invalid inputs (000, no input)
    • Minimum size of the main window has changed from (878 x 797) to (878 x 850)
  • To accommodate this update, MorphingApp.py's verifyBlendValue() has been changed to verifyValue(str)
    • This new function is now used for checking both full blend and gif values

Version 0.2.7.0 - (2020-08-22)

Known Bugs

  • The GUI can sometimes become unresponsive during morphing calculations
    • QtCore.QCoreApplication.processEvents() is a potential workaround but currently produces buggy results

Added

  • Morphed Image Smoothing
    • A toggleable median filter is now applied to every blend and full blend
      • This filter attempts to remove hot pixels in morphed images by utilizing neighboring pixel values instead
      • Very little to no impact on program performance

Comment: Since this isn't always necessary (and very slightly degrades image quality), it is disabled by default.

Changes

  • The GUI now displays the current image frame being generated when full blending is enabled
  • Morphing.py's getPoints() no longer calculates the minimum X and Y values for creating a sub-mask - this is now handled during initialization of Triangle objects to save computation time during blending
  • Removed two more sub-module imports (Qt's QGraphicsScene & QGraphicsView) that were no longer in use
  • Optimized out a line in MorphingApp.py's updateAlpha() and blendImages()
    • Comment: PyCharm's code inspector may finally relax (for now).
  • Simplified four instances of syntax for defining the interpolated images
    • np.arange(0, self.leftImage.shape[0], 1) → np.arange(self.leftImage.shape[0])

Fixes

  • Corrected an instance where the notification bar stated that the program was blending was in RGB mode instead of RGBA

Version 0.2.6.1 - (2020-07-27)

Changes

  • Optimizations to Morphing.py's loadTriangles() and getPoints() functions
    • Declarations, re-assignments, loops, and return statements have been streamlined to be more efficient
  • Optimized MorphingApp.py's blendImages() function to no longer have to [repeatedly] deepcopy each array slice
    • Initialization of a Morpher object will now deepcopy the passed arguments to its left and right image variables
  • Overhauled README.md to now support this program's releases
  • Once again, removed more unnecessary module dependencies that were accidentally included in the last commit

Fixes

  • Fixed a bug where .png (RGBA) images could only be morphed into .jpg images (RGB)

Version 0.2.6.0 - (2020-07-21)

Changes

  • Optimized out several for loops (both nested and not) in blendImages() in favor of more efficient NumPythonic syntax
    • Image deconstruction is now handled by dimension slicing, i.e., "leftColorValueR = leftARR[:, :, 0]"
    • Image reconstruction is now handled by this simple one-liner: "np.dstack((blendR, blendG, blendB, [blendA]))"
    • Comment: When recreating color images, the program will work with 3 or 4 array slices which later need to combine into one final image. My initial (inexperienced) approach was to create for loops in order to read the initial layers of data from the two images into multiple lists (which were then converted and reshaped into arrays) and then to later create double for loops in order to iterate over the blended array data and append it all into one ordered list (which was then converted and reshaped into an array for image processing)... obviously, this lengthy explanation should highlight that this was all a very inefficient use of space and time. This has now been remedied.
  • Removed several module dependencies as well as the main block in Morphing.py in preparation for this program's first release candidate

Fixes

  • Resolved a known issue with point placement via the GUI where points were [often vertically] off center
    • Comment: TL;DR: The corner coordinates and scalar values (!!) for the left and right images weren't being calculated accurately enough. The methodology I adopted for this implementation (with the resize update) was a bit sloppy anyways, so variables such as self.leftMinX, self.rightMaxX, self.maxY, and more have all been removed in favor of more dynamic Qt methods - e.g., self.startingImage.geometry().topLeft().x().
  • Fixed an I/O crash where, if the Images_Points folder didn't exist, the program couldn't create new text files
  • Fixed an index out of bounds crash when accessing images in autoCorner()
  • Fixed a crash where the user could press the blend button before morphing was even enabled
    • Comment: I actually laughed when I accidentally caught this during debugging.. what an entertaining oversight.
  • Fixed a bug where the GUI would fail to visibly update during the morphing process
    • The blend button now locks up, and the notification bar displays a relevant message while the program operates
    • Comment: These things were already happening in the background; visible changes to the QtGui just wouldn't apply due to the fact that the MainWindow "wasn't responding." Qt's repaint() method serves as a simple workaround here.
  • Fixed a bug where regex in loadDataLeft() and loadDataRight() wasn't correctly detecting '.PNG' images
    • ".png$" → ".(PNG|png)$"
  • Fixed a bug where, after invoking resetPoints() with triangleUpdatePref on, the first set of added points would use the wrong size & style before being confirmed
  • Fixed a bug where resetPoints() wasn't properly enabling the Add Corners button of the GUI
  • Fixed a bug where the Reset Points button was sometimes incorrectly disabled after invoking Undo (CTRL + Z) on a newly confirmed set of points
    • "if len(confirmed_x) >= 3:" → "if len(chosen_x) + len(confirmed_x) >= 3:"
  • Fixed a bug where old triangles would still display on the GUI after invoking resetPoints() with triangleUpdatePref on
  • Fixed a bug where, when triangleUpdatePref was on but the triangle box was disabled, triangles were not displaying during certain mousePressEvents
    • Comment: There was an.. admittedly ugly.. triple nested conditional in mousePressEvent() that received a fair amount of cleanup with this update. The point of it was to check if the total number of chosen and confirmed points was at least 3 and then enable triangles - this doesn't work well after a reset with triangleUpdatePref, so now the conditional checks if the total number of added, chosen, and confirmed points is at least 3 (but only invokes when currentWindow is set to the left image).
  • Fixed a bug where invoking autoCorner() wasn't re-checking the triangle box when triangleUpdatePref was on
  • "Fixed" a bug where triangle color values weren't being remembered by the program when disabled/enabled
    • Comment: The moment I began debugging this, the issue mysteriously resolved itself without any lasting change to the source code.
  • Removed an instance where self.currentWindow was unnecessarily set in resetPoints()
  • Removed an instance where refreshPaint() was being invoked twice in resetPoints()
  • Removed the self.resetFlag variable
    • Comment: With this update's implementation of bug fixes for resetPoints(), this variable became obsolete and is consequently being removed.
  • Other general code cleanup

Version 0.2.5.1 - (2020-07-07)

Known Bugs

  • Instances where points added through the GUI are vertically off center
    • Especially apparent after the first blend has been executed (which reshapes the GUI by a bit)

Unreleased

  • Multithreading / multiprocessing implementation for getImageAtAlpha() in order to improve performance
    • Comment: Multiprocessing is applicable to the matrix math while multithreading is applicable to sharing the same image variable for assigning interpolated values.

Changes

  • The morphing algorithm has received a significant increase in performance
    • Matrix multiplication is now conducted across each triangle's entire set of points at once (as opposed to multiplying each individual point at a time)
    • Roughly translates to a 20% speedup
  • Assignment of self.leftInterpolation and self.rightInterpolation has been moved from getImageAtAlpha() to the initialization of the given Morpher object in order to reduce the number of calls to RectBivariateSpline()
  • Moved image interpolation from getImageAtAlpha() to the new interpolatePoints()

Version 0.2.5.0 - Endless Polishing (2020-07-01)

Known Bugs

  • Instances where points added through the GUI are vertically off center
    • Especially apparent after the first blend has been executed (which reshapes the GUI by a bit)

Added

  • Full Blending Input
    • When the full blending box is checked, a new text box is enabled for the user to specify their desired alpha increment to be used for generating and displaying frames
    • The default full blending value is still 0.05 but can now be set as low as 0.001 or as high as 0.25
      • A Qt mask is used to restrict some (but not all) letter and symbol input - format is [0-1].[0-9][0-9]{0,1,2}
      • The program handles all other invalid inputs (1.0, 0.000, etc)
    • The alpha slider and it's tick marks will now dynamically change in response to changes to the full blend value
      • The program will intelligently modify the user's given value to one that best fits the slider
        • For example, if given 0.14, the program will automatically change it to 0.142 for the user's convenience
  • Reset Alpha Slider
    • Since the user is now able to cause changes to the alpha slider's parameters, there is now a reset button for it
      • Resetting the alpha slider will also reset the full blend value to default as well

Changes

  • Revised and created additional tooltips in MorphingGUI.ui and MorphingGUI.py
  • Revised when self.fullBlendComplete is set to False in order to safely accommodate feature update
    • For example, after a full blend, changing the input value disables self.fullBlendComplete
  • Revised all five array declarations in Morphing.py's getImageAtAlpha() so that they no longer require reshaping afterwards
  • Rewrote x & y min/max statements in Morphing.py's getPoints() to be more NumPythonic
    • "min(self.vertices[0][0], self.vertices[1][0], self.vertices[2][0]))" → "self.vertices[:, 0].min()"
  • Rewrote a conditional in loadDataLeft() and loadDataRight() to check whether the two images are on instead of whether the two images have scaled contents
    • Comment: This effectively doesn't change behavior at all, but it makes more sense to be written this way. If the two images have scaled contents, they're both on anyways.. so just check for that.
  • Moved all "self.triangleBox.setEnabled(0)" lines to come after "self.triangleBox.setChecked(0)" instead of the other way around
  • Updated the GUI's MainWindow icon from Qt's default icon to "Morphing.ico"
    • Comment: This is currently locked at an awkward size of 24 x 24... If that can't be changed, the icon will be redesigned. Still attempting to set an application icon as well.
  • Optimized: Removed 3 SLOC each from MorphingApp.py's keyPressEvent() (2x), resetPoints(), loadDataLeft(), and loadDataRight()
    • "if tri.isChecked(): triPref = 1 else: triPref = 0" → "triPref = int(tri.isChecked())"
  • Optimized: Removed 24 SLOC (26% reduction) from MorphingApp.py's MainWindow constructor. All GUI initializations have been shifted from MorphingApp.py to MorphingGUI.ui & MorphingGUI.py.
    • Comment: These were pretty much just a waste of space - some lines accomplished nothing at all. Other times, MorphingGUI.ui and MorphingGUI.py were enabling elements just for the initializer to immediately disable them afterwards.
  • Optimized: Removed 7 SLOC (32% reduction) from MorphingApp.py's updateTriangleWidget()
    • "if val: x.setEnabled(1) else: x.setEnabled(0)" → "x.setEnabled(val)"
  • Updated README.md

Fixes

  • Fixed an issue where the right image's triangle vertices were being loaded from type List instead of type Array
  • Fixed an issue where tooltips could appear over blank space, as some GUI elements were "wider" than they actually were
  • Fixed a bug where loading new images with Show Triangles checked would break both the checkbox and the triangle painter
    • Comment: This was a particularly nasty bug. It ended up mainly being caused by a state change method that triggered whenever the triangle box was modified. This method was setting flags at the same time as the calling block, which led to really weird and unpredictable behavior that was difficult to debug.
  • Removed an event signal where invoking loadDataLeft() would simultaneously invoke displayTriangles()
    • Comment: This may have also contributed to the aforementioned bug, above.
  • Removed several imported modules that were no longer being utilized by the program
  • Fixed a bug where the user's triangle preference wasn't being set to 0 when the box was unchecked
  • Fixed a bug where autoCorner() had to place corner points that were off by a pixel
    • (1, 1), (1, y - 1), (x - 1, 1), (x - 1, y - 1) → (0, 0), (0, y), (x, 0), (x, y)
  • Replaced a global (and hard-coded) DataPath variable that wasn't being used with a dynamic root directory variable. Hard coded text file paths in loadDataLeft() and loadDataRight() now instead build on ROOT_DIR
    • Comment: Now this program can start having release candidates! Once module import woes get sorted out, anyways.
  • Removed an unnecessary call to refreshPaint() in loadDataLeft() and loadDataRight()
  • Removed a conditional in both loadDataLeft() and loadDataRight() that wasn't necessary
    • "if self.leftOn == 0: self.leftOn = 1" → "self.leftOn = 1"
  • Removed a chained conditional in both loadDataLeft() and loadDataRight() that wasn't being used
    • "if x is None; else if x is PNG; else" → "if x is None; else"
      • Comment: In this pseudo-example, x is the return of a regex search for ".png", so it will either be None or it won't. Both cases are indicative of what behavior should follow, so there is no need for a second conditional.
  • Removed two nested conditionals in loadDataLeft() and loadDataRight() that could have been simplified into the parent
    • "if x == y: if x >= 3: if y >= 3: ..." → "if x == y >= 3"
  • Removed a conditional in resetPoints() that wasn't necessary
    • Comment: Checking for whether the two images are on is redundant in this case as the two images must be on for resetPoints() to be called anyways.
  • The self.fullBlendComplete flag now gets set to 0 after a normal blend is executed
    • Comment: This was a harmless oversight at the time of full blending's creation but became a potentially dangerous bug with the addition of user input.
  • Other general code cleanup

Version 0.2.4.0 - (2020-06-27)

Added

  • Full Blending
    • The user can now check the Full Blend box before clicking blend to have the program generate a frame for each 0.05 alpha increment blend of the two images
    • Once the program finishes full blending, the user can use the alpha slider to display (in realtime) the corresponding frame for each alpha increment.

Comment: Since full blending renders 21 frames instead of 1, it is naturally slower than normal blending and, because of this, is disabled by default.

Changes

  • Additional performance improvements to MorphingApp.py's paintEvent():
    • Removed all calls to set the QPen and its color when drawing points
      • Comment: The QPainter by itself is more than enough for this task.
  • Added notification line condition for toggling the Full Blend box:
    • "Successfully enabled/disabled full blending."
  • Updated README.md

Removed

  • Removed unrelated and unnecessary folders/files from the repository (oops!)

Fixes

  • Removed an extra (unnecessary) call to refreshPaint() in mousePressEvent()

Version 0.2.3.0 - The Cleanup Update (2020-06-26)

Unreleased

  • Potential incomplete fix for interpolation performance by way of manual pixel calculation (instead of using scipy's RectBivariateSpline function, which is ~58% of this program's runtime)

Changes

  • Completely rewrote MorphingApp.py's paintEvent() to be far more efficient and much smaller (91 lines removed - a 60% reduction in SLOC)
    • Added "resize" and "change" flags to prevent paintEvent from unnecessarily triggering
      • Resize performance (especially with larger/color images) has been significantly improved by this
    • Restructured the function to try drawing triangles first (with a conditional) and then draw points after (instead of either drawing all triangles and all points or only drawing all points)
      • This reduces the size of the function without changing behavior
    • Moved all re-declarations of QPainter, QPen(), and it's width to initialization; removed unnecessary QPainter.end() calls
      • Comment: At the time of creation of Version 1.0, there could only be one QPainter at a time (for whatever reason, most likely through fault of my own), so multiple initializations/terminations were required. Version 0.2.3.0 can have multiple QPainters active at a time and, consequently, has no need for this behavior.
    • Removed all "if QPainter.isactive" checks
      • Comment: As far as I'm aware, these never did anything to begin with.
    • Removed unnecessary "changes" to the left/right images after painting
      • Enabling the image and the scaling of its contents is handled by the image loaders and doesn't need to be here
  • Slightly modified how the notification line reacts to changes with the red/blue/green sliders as well as blend status
    • "Red value changed from X to Y." → "Red value set to X."
    • "Morph took X seconds." → "RGB morph took X seconds."

Fixes

  • Interpolated data is now saved directly to the image variables in Morphing.py's getImageAtAlpha()
    • The function was assigning all final interpolated image values to a left and right "copy", which was then saved to the corresponding image variables. This "copy" was redundant and unnecessary, so it has been removed.
  • New points are now added directly to the confirmed list in MorphingApp.py's autoCorner()
    • Similar to the fix above, newly added points were being appended to the list of temporary points and then immediately popped out and appended to the list of confirmed points. This "copying" behavior was, again, redundant and unnecessary, so it has been removed.
  • Removed a redundant nested conditional in resetPoints() where both conditions disabled deletion and called the painter
  • Oftentimes, paintEvent() was being invoked twice for each change (a relic of Version 1.0's poorly implemented painter behavior). This has been fixed in the following functions:
    • autoCorner()
    • resetPoints()
    • refreshPaint()
      • Comment: Obviously, this one is a big deal. Paint events were being called twice as often as was necessary, so resolving this issue has significantly improved GUI performance (especially with the RGB sliders).
  • Removed all remaining instances of self.update(), self.leftUpdate, and self.rightUpdate
    • Comment: These were relics of Version 1.0's poorly implemented painter behavior (self.update() may have even been redundant). Truthfully, I don't know what self.update() was even supposed to do - even then - as I can't find anything about it online. There may have been a time when it somehow invoked paintEvent(), but this is no longer needed (as a new function expressly exists for this purpose). Thus, self.update() has been replaced with self.refreshPaint() for clarity's sake.
  • Instances where other functions were directly calling paintEvent() have been replaced with calls to refreshPaint() instead
  • Other general code cleanup

Version 0.2.2.0 (2020-06-21)

Added

  • Triangle Widget
    • When the user enables Show Triangles, there is now an accessible widget that provides RGB sliders to change the color of the triangles in realtime

Comment: This is strictly a QoL change for users, whether they have a preferred color or that a different color may be easier to see on top of the two images. Clarity is key.

Changed

  • Changed the following elements in MorphingGUI.ui and MorphingGUI.py:
    • Changed the main window's title from "MainWindow" to "Python Image Morpher"
    • Minimum size of the main window has changed from (630 x 611) to (878 x 797)
    • Minimum size of the image panels has changed from (300 x 225) to (424 x 318)
    • Added labeled red, green, and blue sliders to the "Morphing Functions" container
      • These sliders respectively invoke the new updateRed(), updateGreen(), and updateBlue() functions, which are used to modify the painter

Fixes

  • The blend button is now disabled during the time that the program is executing a blend command

Version 0.2.1.0 (2020-06-20)

Added

  • Notification Bar
    • The GUI now displays an information panel at the bottom left that outputs the status of the user's actions
      • For example, using Add Corners will evoke either: "Successfully added X new corner point(s)" or "Failed to add any new corner points."

Changed

  • Added notification conditions for:
    • Adding corners
    • Resetting points
    • Pressing backspace
    • Entering CTRL+Z
    • Entering CTRL+Y
    • Toggling transparency
    • Modifying alpha
    • Loading images
    • Blending images

Removed

  • Removed all console print statements

Fixes

  • Add Corners no longer creates additional points that already existed

Version 0.2.0.0 - The GUI Overhaul (2020-06-17)

Added

  • Resizing
    • The user is now able to freely resize the window along with its contents
      • Image windows will now dynamically resize themselves to be as large as possible
  • Triangle Preference
    • The program now remembers the user's preference for the "Show Triangles" setting
      • If an action causes this setting to become disabled, the program will re-enable it the next time it is available
  • Transparency Blending Toggle
    • The user is now able to specify whether they want to blend the alpha layer of images
      • This setting only affects .PNG images

Changed

  • Changed the following elements in MorphingGUI.ui and MorphingGUI.py:
    • Applied a grid layout to the main window
      • Restructured the entire GUI based on the grid layout
    • Added a spacer to the main window
    • Added a "Morphing Functions" container for buttons, sliders, etc.
    • Added self.transparencyBox to the main window
    • Minimum size of the main window has changed from (740 x 705) to (630 x 611)

Removed

  • Removed the following elements from MorphingGUI.ui and MorphingGUI.py:
    • self.startingFrame
    • self.endingFrame
    • self.blendingFrame
    • self.startingImageTriangles
    • self.endingImageTriangles

Comment: This update rewrote paintEvent() such that the input images no longer need a separate triangle layer. The frames served as containers for both the images as well as their respective triangle layers (in this regard, there was no point in self.blendingFrame anyways). Thus, all of these elements are being removed.

Fixes

  • Removed instances of self.update() when loading images
  • Other general code cleanup