Simple commandline tool & header-only library to create, batch-apply and convert 3D LUTs.
- Rip color toning filters from any of your favorite apps with zero or very little accuracy loss, even if the app only supports saving as low-res lossy JPEGs
- Make your own filters for once and generate perfect 3D LUTs from them
- Ultra-fast exportation to
.cube
format, which can be used with many professional software such as Photoshop, Lightroom & Lightroom Mobile, Premiere, Final Cut Pro and DaVinci Resolve - Quickly apply 3D LUT filter to a batch of images with multi-thread processing, without having to open-apply-save each one of them manually in editors
- Have the above facilities in your own C++ project by adding some headers
In this section I'd prefer to use a more intuitive language to explain the usage as the tool is initially made for my artist friends :D
Go to the release page and grab the latest release, it contains the stuff you'll need to perform the following missions.
You'll need to use your filter to process a specially designed image, the lutmap:
- Simply apply the filter to the lutmap and save it as PNG format to get the zero-loss result
- If your app doesn't support PNG, you may still get a decent result! The lutmap was originally designed to rip beautiful filters from my social apps, which will shrink my image size and save as a lossy JPEG.
- For even better results with JPEG, you are still encouraged to manually chop the lutmap 2-by-2 or 4-by-4 (without losing pixels), enlarge each of them by 2 or 4, process them with the same filter, then put them back together seamlessly to get a complete lutmap. This way you'll get rid of some JPEG noise, then a 4px (or 8px, depending on how much you've enlarged the pieces) gaussian blur will also help smoothing out the remaining subtle noise. Remember to resize back to 4096 by 4096, because LUTools only accept this size for a 3D LUT. You can do all these procedures within Photoshop, Krita or GIMP.
The processed lutmap contains everything about the filter, it can be used to export .cube
files, or just to apply to any other image.
Batch processing images with a given filter is as simple as mouse selections and drags:
-
Select all the images you want to process by holding Ctrl and clicking
-
Add the processed lutmap to your selection
-
Click and hold your mouse on the lutmap that you've selected last, drag and drop onto the LUTools executable.
Let me explain this: By doing so, you are dragging all other images together to the LUTools, which is actually passing the paths of all these files as a list, into LUTools. And the fact that you're dragging your mouse cursor on the lutmap means passing its path by the first parameter.
-
A terminal ("black window") will appear, processing all images using the filter described by the lutmap. Results will be saved to the same folder.
Well, forgive me that this must be done via the terminal. But I promise it will be easy:
-
Open the "Run" dialog by pressing Win + R
-
Type
cmd
into the dialog and press enter, now you've opened the terminal -
Type
cd /d
then a space, then the directory where you've put the LUTools executable, press enter to run -
Type
LUTools -cube
then a space, then the path to your filter (the processed lutmap), run itTips: It will be much more convenient if you put the LUTools executable and the filter in the same folder -- you'll only need to type the filter's filename.
And a
.cube
file will be generated in-place, ready to be used. -
Alternatively, you may type
LUTools -cube 64
to have the file generated with a higher resolution of 64. This works for more complicated LUTs. The default resolution is 25 as you might have seen.
What is that .lut
file generated in the filter's directory?
-- That is the filter's cache file. LUTools uses it to accelerate filter loading, so that next time you use the same filter, the loading will be much faster. It's totally safe to delete it, because it can be regenerated.
git clone --recurse-submodules git@github.com:lfod1997/LUTools.git
It is a CMake Project with only one target so just configure and make it.
Usually, you'll need to configure the CMake project in order to generate the compile_commands.json
that Clangd requires:
-
Windows (MinGW or any variant):
mkdir build cd build cmake -G "MinGW Makefiles" -D CMAKE_EXPORT_COMPILE_COMMANDS=ON ..
-
Windows (MSVC):
Using the MSVC toolchain with Clangd requires additional tooling, follow this guide; or I'd rather recommend using Visual Studio with CMake support.
-
Linux / macOS (GCC):
mkdir build cd build cmake -D CMAKE_EXPORT_COMPILE_COMMANDS=ON ..
It's okay for both platforms that the
compile_commands.json
is generated atbuild/
instead ofsrc/
because clangd will try various paths and we have a flat project structure.
LUTools {LUT | LUT_MAP} [-cube [RESOLUTION]] [INPUT [-OUTPUT]]...
Where LUT stands for the generated .lut
file; LUT_MAP stands for any processed (or unprocessed) lutmap.
- Optionally,
-cube
may be used with or without a RESOLUTION specified. The generated.cube
file will contain RESOLUTION ^ 3 samples. Default resolution is 25. - Optionally, any number of INPUT images may be passed, they will be processed using the specified LUT. If no OUTPUT is specified for the INPUT, the output file will be put in the same directory, with a suffix
_
followed by the filter being used, and in the same image format as the INPUT. - Each INPUT may have an OUTPUT after it to explicitly specify the output path. This syntax requires a
-
prefix, otherwise I can't tell the difference :D
#include
the headers in the src
directory, and have the functions in your project.
Generally you'll just need these:
Lutools::Color* Lutools::cacheLUTMap(const std::string& input_file, const std::string& output_file)
inlut.hpp
Lutools::Color* Lutools::loadCacheFromFile(const std::string& path)
inlut.hpp
void Lutools::generateCube(const Lutools::Color* data, int cube_res, const std::string& output_file)
incube.hpp
All functions are carefully documented so I won't bother speaking here.
Namespace Lutools
:
color.hpp
contains a simple RGBA classimage.hpp
contains a simple image wrapper that supports image loading and writinglut.hpp
supports analyzing lutmaps and cache IOcube.hpp
supports exporting.cube
files
Namespace Pathutils
: only pathutils.hpp
, contains simple functions I used to process paths. If the file bothers you, just combine it into some of the other headers :D
Namespace LutoolsCli
: only thread_guard.hpp
, contains a simple wrapper of threads, used by the CLI.
This image called lutmap is designed to get the colors mapped by any 3D LUT (obviously), with some tricks against JPEG compression:
- The image has relatively low spatial frequencies, resulting in lower JPEG noise
- Very high redundancy, actually enumerating the entire colorspace, so partial detail damage can be easily recovered (a gaussian blur, or more preferably surface blur, will do)
- While being 4K, the image data block is extremely regular, resulting in small PNGs
This project uses the amazing stb image library by nothings.