This File is part of bLUe software.
Copyright (C) 2017-2023 Bernard Virot
bLUe is a layer-based image editor. It aims to integrate a new perceptual 3D LUT editor with more traditional tools to provide a powerful GUI for photo editing. The program is fully modular : tools are implemented as independent adjustment layers using a common GUI. New features can be added easily: any imaging library exposing Python bindings can take advantage of the GUI.
bLUe can develop raw images in all usual formats : nef, cr2, arw, dng, ... It supports dng/dcp dual illuminant camera profiles : they are essential for rendering colors similar to that produced by camera software.
bLUe provides drawing layers and paint brushes with adjustable parameters: size, flow, hardness, opacity.
bLUe is aware of multiscreen environments and color profiles : it uses image and monitor profiles in conjunction to display accurate colors.
bLUe uses a pretrained neural network to provide automatic 3D LUTs for fast enhancement of images. The pretrained model and the code are taken from the recent work of Hui Zeng, Jianrui Cai, Lida Li, Zisheng Cao, and Lei Zhang.
Images being edited and their layer stack can be saved to .blu file format.
The program is written in Python.
A 3D LUT is a table representing a 3D cube of color nodes. Image pixels are associated with nodes, according to their color. Changes are applied to each node individually, giving full control over colors.
3D LUTs are edited in a perceptual editor by selecting, grouping and moving color nodes over a hue-saturation color wheel. Nodes are bound to an elastic grid and a grid smoothing algorithm is provided to even changes in color.
A group of nodes represents a meaning region of an image, for example : tree leaves, skin, hairs, water, blue sky, ...), and all nodes in a group should be edited in a similar way.
The editor's workflow is based on the grouping of nodes.
-
Nodes are selected and grouped from the image;
-
To edit the colors of all pixels in a group while keeping their brightnesses, simply move the group on the color wheel;
-
To control the brightnesses of pixels in a group, edit the brightness curve of the group.
The whole grid can be warped to produce a particular look (orange-teal, moonlight, ...)
Color transformations such as RGB curves, channel mixing, temperature,... can be easily integrated in the 3D LUT, in any order, by adding the corresponding adjustment layers to the stack and recording the whole stack as a single 3D LUT. An example 3D LUT can be found on the web site.
See the bLUe site for screenshots, tutorials and user manual.
- Neural-network-based automatic 3D LUT for image enhancement
- Soft proofing
- Simultaneous edition of multiple images in formats jpg, heic, png, tif, nef, cr2, arw, dng,...
- Color profile management
- Adjustment layers : exposure, brightness, saturation, contrast, channel mixer, color temperature, inversion, filters, color grading.
- noise reduction, seamless cloning, segmentation, exposure fusion, curves, 2.5D LUTs, 3D LUTs.
- Drawing and painting layers; tablet support
- Extensible set of brushes and patterns; .abr files support
- Automatic contrast enhancement (histogram warping and CLAHE)
- Seamless cloning
- Exposure fusion
- Multiple blending modes; adjustable layer opacity
- Import and export of 3D LUTs in .cube format
- Editable masks
- Automatic import of camera specific profiles for development of raw images
- Built-in file explorer
- Slide show
- Context-sensitive help
- Python >= 3.9
- Qt6 for Python (PySide6)
- opencv-python
- numpy
- Pillow
- RawPy
- PyWavelets
- PyTorch >= 1.4 and torchvision for auto adaptive 3D LUT
- tifffile
ExifTool should be installed.
On Windows, pywin32 is needed for multiscreen management.
On Ubuntu, gi and colord are needed to manage monitor profiles.
On MacOS, pyobjc is needed to use the ColorSync framework.
Edit config_win.json (Windows) or config.json (other OS) to specify system paths and options.
This project is licensed under the LGPL V 3.
Author: Bernard Virot
The "bLUe" editor is a Python-based platform designed to simplify the development and testing of image processing techniques. Leveraging PySide6 for GUI construction and Numpy for computational tasks, it provides a modular, layer-based architecture that enables real-time parameter adjustments and seamless integration with Python-compatible image libraries. With features like stackable layers, advanced color management, and an intuitive GUI, "bLUe" facilitates both professional and experimental workflows for image editing. Its extensibility allows users to incorporate new image processing techniques with ease, making it a robust tool for developers and researchers alike.
"bLUe" is a powerful editor for 3D LUT (Look-Up Table) manipulation, designed to bridge the gap between advanced image processing and user-friendly interfaces. Built on Python technologies such as PySide6 and Numpy, the platform offers a modular, extensible framework that supports Python-compatible libraries. By allowing real-time visualization of adjustments, "bLUe" enhances user productivity and enables rapid experimentation with image processing workflows.
"bLUe" employs a layer-based processing system. Each layer represents a processing step : it processes an input image, and stores the output image. A layer can be visible or hidden. Each layer holds an opacity value, a blending mode, and a mask. Several layers can be stacked. They act as a pipeline, computing the final image by blending the visible layers, from bottom to top.
A background layer, holding the initial image, is added at the bottom of the stack, and all layers must be the same size.
For large-scale images or computationally intensive tasks, a preview mode speeds up processing without compromising the workflow.
The GUI is designed for seamless interaction with processing parameters. Each layer has a dedicated view that reflects real-time changes, ensuring immediate feedback. The platform’s user-friendly interface is built with Qt for Python (PySide6), offering features like contextual help for accessibility.
The editor’s core data structures include:
- bImage: Extends QImage to handle image processing workflows.
- QLayer: Represents a processing layer, inheriting from QImage and providing transformation methods.
- baseForm and baseGraphicsForm: Simplify GUI interactions, supporting standard widgets and graphical elements, respectively.
- imImage: Extends bImage to handle the stack of processing layers.
The following UML diagram illustrates the relationship between these classes:
classDiagram
class `imImage`
imImage: layersStack
class `bImage`
class `baseForm`
class baseGraphicsForm
class `QLayer`
QLayer: execute
QLayer: rPixmap
QLayer: parentImage
QLayer: getGraphicsForm()
QLayer: getCurrentImage()
QLayer: getCurrentMaskedImage()
QLayer: inputImg()
`bImage` --> `QLayer`
`bImage` --> `imImage`
The following Python code snippet illustrates how layers are blended to produce the final output image:
def getCurrentMaskedImage(self):
"""
Blend the layer stack up to the current layer (included), considering
blending modes, opacities and masks.
:return: Blended image
:rtype: bImage
"""
# Initialization
if self.maskedThumbContainer is None:
self.maskedThumbContainer = bImage.fromImage(self.getThumb(), parentImage=self.parentImage)
# Reset container
img = self.maskedThumbContainer
img.fill(QColor(0, 0, 0, 0))
# Blend layers
qp = QPainter(img)
for layer in self.parentImage.layersStack[:self.stackIndex + 1]:
if layer.visible:
qp.setOpacity(layer.opacity)
qp.drawPixmap(QRect(0, 0, img.width(), img.height()), layer.rPixmap)
qp.end()
return img
This approach ensures efficiency by maintaining containers that are instantiated only once and updated dynamically.
Adding a new technique involves three steps:
Create a form for user interaction, leveraging existing forms as templates. For example, use graphicsFormAuto3DLUT.
Input items like sliders and checkboxes can be customized as needed.
- Add items to the form attributes :
self.slider1 = QbLUeSlider(Qt.Orientation.Horizontal)
...
- For each item, connect the signal indicating that a new state or a new value is available to self.dataChanged :
for s in sliders:
s.sliderReleased.connect(self.dataChanged)
Note. To be able to save and restore their state, input item classes must override the methods _getstate()_ and __setstate()__.
Define the transformation function in the QLayer class (or a subclass). For example, the following function builds a 3D LUT for automatic image enhancement. It uses three user-defined parameters for testing purpose.
def applyAuto3DLUT(self):
# Get the user interface
ui = self.getGraphicsForm()
# Generate an automatic 3D LUT, applying three correction coefficients.
coeffs = [ui.slider1.value(), ui.slider2.value(), ui.slider3.value()]
lutarray = generateLUTfromQImage(self.inputImg(), coeffs)
# Interpolate the input image with LUT
interp = chosenInterp()
outputArray = interp(lutarray, self.inputImg())
# Copy outputArray to the image data buffer (alpha channel excluded)
QImageBuffer(self.getCurrentImage())[..., :3] = outputArray
# Convert the image to QPixmap.
self.updatePixmap()
Add the new functionality as an action in the menu Layer:
elif name == 'actionAuto_3D_LUT':
layer = window.label.img.addAdjustmentLayer(role='AutoLUT')
layer.execute = lambda: layer.tLayer.applyAuto3DLUT()
A dedicated class (QPresentationLayer) handles advanced color management. It enables compatibility with image and monitor profiles, ensuring accurate color rendering.
The "bLUe" editor offers a cutting-edge environment for developing and testing image processing techniques. Its modular design, real-time visualization, and seamless extensibility make it a powerful tool for both developers and researchers.