-
Notifications
You must be signed in to change notification settings - Fork 596
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Initial JPEG XL support for image input/output #4055
The head ref may contain hidden characters: "JPEG\u00A0XL"
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You have this marked as "draft", so I know you aren't seeking a final review, but I thought I'd add some comments at this stage. Mostly just some organizational tips.
I'm not commenting yet on the code in jxlinput.cpp, because I figure it's all still in-progress. I also assume that at some point, you will add something to the testsuite that exercises this code so we have confirmation that it correctly reads jxl files.
src/cmake/modules/FindJXL.cmake
Outdated
@@ -0,0 +1,27 @@ | |||
# Find libjxl |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please put our usual 3-line copyright boilerplate here
src/cmake/modules/FindJXL.cmake
Outdated
# Find libjxl | ||
# Will define: | ||
# - JXL_FOUND | ||
# - JXL_INCLUDE_DIRS directory to include for libjxl headers |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe the more standard name for this is JXL_INCLUDES
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This comment still applies
src/cmake/modules/FindJXL.cmake
Outdated
if(JXL_FOUND) | ||
set(JXL_LIBRARIES ${JXL_LIBRARY} ${JXL_THREADS_LIBRARY}) | ||
set(JXL_INCLUDE_DIRS ${JXL_INCLUDE_DIR}) | ||
endif(JXL_FOUND) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If this is all found, in addition to those variables, please also set up targets in the modern CMake style. You can find an example of this in our FindLibheif.cmake module.
Also, it is important for this module to set a JXL_VERSION so we can check version range. A good example of how to extract this from header files can be found in our FindOpenVDB.cmake
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This comment still applies
src/jxl.imageio/jxl_pvt.h
Outdated
class JxlInput final : public ImageInput { | ||
public: | ||
JxlInput() { init(); } | ||
~JxlInput() override { close(); } | ||
const char* format_name(void) const override { return "jxl"; } | ||
int supports(string_view feature) const override | ||
{ | ||
return (feature == "exif" || feature == "ioproxy"); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would say that by default, the definition for JxlInput class should be in jxlinput.cpp if that's the only place that reads it.
Some of our format plugins have a format_pvt.h that contains definitions that will be needed by both the input and output classes. You may want to wait until there is an implemented output (when you will know what needs to be shared) before making an extra header for the shared definitions.
Signed-off-by: Peter Kovář <peter.kovar@reflexion.tv>
Signed-off-by: Peter Kovář <peter.kovar@reflexion.tv>
It's started failing on GHA and is old enough that it's not worth trying to track down what's broken. We still have MacOS 12 and 13 tests in the matrix. Signed-off-by: Larry Gritz <lg@larrygritz.com> Signed-off-by: Peter Kovář <peter.kovar@reflexion.tv>
…Foundation#4056) Signed-off-by: Larry Gritz <lg@larrygritz.com> Signed-off-by: Peter Kovář <peter.kovar@reflexion.tv>
Switch "bleeding edge" test to gcc13, "latest releases" test to gcc12 Signed-off-by: Larry Gritz <lg@larrygritz.com> Signed-off-by: Peter Kovář <peter.kovar@reflexion.tv>
…ainst (AcademySoftwareFoundation#4058) Signed-off-by: Larry Gritz <lg@larrygritz.com> Signed-off-by: Peter Kovář <peter.kovar@reflexion.tv>
…n#4057) Used to be no problem, but after a recent VSCode update, this seems to have been causing errors when asking VSCode to format. No idea why this recently became problematic. Signed-off-by: Larry Gritz <lg@larrygritz.com> Signed-off-by: Peter Kovář <peter.kovar@reflexion.tv>
…ademySoftwareFoundation#4060) The important bug fix is in oiiotool image input when autocc is used, where we reversed the sense of a test and did the autocc-upon-input if the file's color space was equivalent to the scene_linear space (pointlessly), and skipped the conversion if they were different (oh no, big bug!). Along the way: * Add missing try/catch around OCIO call that should have had it. * Some very ugly SPI-specific recognize-by-name color space heuristics. I hate this, sorry, but it solves a really important problem for me. * A bunch of additional debugging messages during color space inventory, enabled only when debugging, so nobody should see these but me. * A couple places where we canonicalize the spelling of "oiio:ColorSpace". Note that there is still an issue with the color space classification using OCIO 2.3+'s identification of built-in equivalents by name. These are shockingly expensive. I will return to this later. Signed-off-by: Larry Gritz <lg@larrygritz.com> Signed-off-by: Peter Kovář <peter.kovar@reflexion.tv>
Signed-off-by: Larry Gritz <lg@larrygritz.com> Signed-off-by: Peter Kovář <peter.kovar@reflexion.tv>
…areFoundation#4062) A recent change (PR 4026) tried to include Imath.h in cases where there was no SSE support (to provide a fallback implementation of matrix invert, because simd.h's native one requires SSE), but it had a mistake -- it used `#if !OIIO_SIMD_SSE` prior to that symbol being defined! This caused the header to always be included, which is not what we wanted, as we desired for that header to not be exposed in the public headers. This patch offers a temporary/partial fix by simply moving that guarded include later in the file to after OIIO_SIMD_SSE is defined. For architectures that support SSE, at least, this achieves our goal of not exposing Imath.h in public headers. I need this patch right away to solve a particular problem I'm running into. Longer term, we note that this isn't a full solution -- on non-SSE architectures (like ARM), it still ends up exposing Imath.h against our desire. For today, so be it. A better fix would be to rewrite our current SSE-requiring invert to change the SSE intrinsics therein to the wrapped kind we use with our simd classes, which will let it work fine with ARM. But I feel like that needs to be done carefully and with more testing than I can afford at this minute when I need this fix. I will return to it shortly, but need this patch now. Signed-off-by: Larry Gritz <lg@larrygritz.com> Signed-off-by: Peter Kovář <peter.kovar@reflexion.tv>
…oundation#4061) - Fix perceptual test false positive by removing the initialization of `cr` to avoid losing the information when leaving block scope Included additional test with parameter `-fail 1`, which was giving a false positive pass for a perceptual difference. This was due to the fact that the result variable `cr` was getting out of scope for the perceptual calculation and the first `if (cr.nfail <= imagesize_t(allowfailures)) {` was testing for the result of the absolute difference instead of the perceptual difference. Before: ``` Comparing "img1.exr" and "img2.exr" PASS ``` After: ``` Comparing "img1.exr" and "img2.exr" 64 x 64, 3 channels Mean error = 0 RMS error = 0 Peak SNR = 0 Max error = 1 @ (5, 17, R) values are 0.1, 0.1, 0.1 vs 0.1, 0.6, 0.1 0 pixels (0%) over 0.008 121 pixels (2.95%) over 1 121 pixels (2.9541%) failed the perceptual test FAILURE ``` Signed-off-by: Aura Munoz <aura.munoz@autodesk.com> Signed-off-by: Peter Kovář <peter.kovar@reflexion.tv>
…wareFoundation#4063) Add a few more assertion checks and error messages in imagebufalgo_test. I observe that if somebody runs the testsuite with `$OCIO` set to a config that doesn't contain the required color space names for this test, it will fail. As a backstop, use the default built-in config if the initial request fails. That will always give a working processor between these spaces if OCIO >= 2.2 is being used. Also one more tiny SPI-specific enhancement to hacky identification of nonconforming color space names in our old configs. Signed-off-by: Larry Gritz <lg@larrygritz.com> Signed-off-by: Peter Kovář <peter.kovar@reflexion.tv>
…twareFoundation#4065) Slightly different output to match for the combo of OCIO >= 2.2, but simultaneously Python 2.7 -- which was not in our CI test matrix, so I missed it, but ran into it at work. Signed-off-by: Larry Gritz <lg@larrygritz.com> Signed-off-by: Peter Kovář <peter.kovar@reflexion.tv>
Don't want that included for Cuda compilation, either. Signed-off-by: Larry Gritz <lg@larrygritz.com> Signed-off-by: Peter Kovář <peter.kovar@reflexion.tv>
…ademySoftwareFoundation#4071) ## simd.h improvements: vint4::load from unsigned char pointer got pre-SSE4 code path. Testing on Ryzen 5950X / VS2022 (with only SSE2 enabled in the build): - vint4 load from unsigned char[]: 946.1 -> 4232.8 Mvals/sec vint4::store to unsigned char pointer got simpler/faster SSE code path, and a NEON code path. Additionally, it got test correctness coverage, including what happens to values outside of unsigned char range (current behavior just masks lowest byte, i.e. does not clamp the integer lanes). - vint4 store to unsigned char[]: 3489.8 -> 3979.3 Mvals/sec - vint8 store to unsigned char[]: 5516.9 -> 7325.3 Mvals/sec NEON code path as tested on Mac M1 Max (clang 15): - vint4 store to unsigned char[]: 4137.2 -> 6074.8 Mvals/sec ## Tests vint4 store to unsigned char pointer got actual correctness checking test, which seemingly was lacking before (only had a benchmark test). Signed-off-by: Peter Kovář <peter.kovar@reflexion.tv>
In some environments, only one kind of the library could be available. Choose proper fallback in such cases. Prefer static library, if LINKSTATIC is ON and dynamic otherwise. Signed-off-by: Dominik Wójt <domin144@o2.pl> Signed-off-by: Peter Kovář <peter.kovar@reflexion.tv>
…ySoftwareFoundation#4073) Without getting the version properly, we weren't disabling use of a deprecated field for OpenJpeg >= 2.5, as we thought we were. This fixes recently broken CI when building against the current openjpeg master. Signed-off-by: Larry Gritz <lg@larrygritz.com> Signed-off-by: Peter Kovář <peter.kovar@reflexion.tv>
…eFoundation#4074) There was a flaw in PNGOutput::write_scanlines when the y range of scanlines to write consisted of less than the whole image, and alpha de-association was needed -- we used the whole-image size instead of the y range of scanlines size. Fixes AcademySoftwareFoundation#4044 Signed-off-by: Larry Gritz <lg@larrygritz.com> Signed-off-by: Peter Kovář <peter.kovar@reflexion.tv>
I didn't have the condition right -- the `if` isn't respected in the `pull_request` section, so this job was running on every PR instead of just the ones with "macarm" in the branch name. Refactor to get the conditional on the job instead. Signed-off-by: Larry Gritz <lg@larrygritz.com> Signed-off-by: Peter Kovář <peter.kovar@reflexion.tv>
This change adds OCIO functionality to iv. To enable OCIO, first set the $OCIO environment variable to point with a path to the OCIO config. "ocio://default" should work as well. If a valid config provided, a new menu gets added under "View/OCIO" with an on/off toggle and submenus to select the image color space and display/view and optional look. If an image color space, display and view are provided as command line parameters ("image-color-space", "display", "view"), then the OCIO mode is switched on automatically. If no image color space is provided as command line parameters, the top most color space of the config get pre-selected in the menus. If no display or view is provided as command line parameters, the default values get pre-selected in the menus. In OCIO mode the exposure and gamma adjustments are done by OCIO. The exposure is done in scene linear, gamma is still in display space. The exposure adjustments don't match between the OCIO and non-OCIO modes. When OCIO fails to initialise the pipeline for any reason, an error message is shown in the status bar, and the non-OCIO path gets used. TODO: - All channel shuffling is done by the existing shader and happens before the OCIO step. We may want to discuss/change that? - The initial image color space gets selected from the command line parameter, or the first color space in the config. There is no attempt being made to use the image metadata. - The exposure/gamma values get baked into the shader, which gets recompiled on every change. We may want to use uniforms instead. - DONE --------- Signed-off-by: Anton Dukhovnikov <antond@wetafx.co.nz> Signed-off-by: Peter Kovář <peter.kovar@reflexion.tv>
…le (AcademySoftwareFoundation#4033) Historically, ImageBuf has provided Iterator for writing to mutable IB's and ConstIterator for reading into IBs. If you initialized an Iterator to an IB that was ImageCache-backed (and therefore by definition not mutable), it would convert the IB to the mutable kind (basically by allocating the memory and reading the file through the IC, thus freeing the IB of its dependence on the cache. An important bug was fixed by PR 3997, which kinda proved that people were not depending on this behavior, since it would tend to fail. But it also got me thinking about to what degree the whole Iterator vs ConstIterator was really necessary, or did it just make things harder for users. In this proposed patch, we change the Iterator behavior so that instead of immediately making the IB writable as soon as the Iterator was instantiated and associated with the IB, we instead treat it as a ConstIterator UNTIL it actually tries to write a value, at which point we ensure that the IB is writable. The bottom line is that this seems to work. If you don't want to have to think about Iterator vs ConstIterator, you don't have to. Iterator is fine. If you don't actually write into any of the pixels, it behaves just like a ConstIterator, including being perfectly happy with an IC-backed IB. Performance metrics indicate that there is NO penalty for doing so -- if you are only reading pixel values, traversing an IB with Iterator is the same speed as doing it with ConstIterator. I had one sleepless night after implementing this when suddenly I realized that this was totally not thread-safe, that somehow multiple iterators traversing the same IB would screw each other up if one of them wrote to a pixel and converted the image. So I put in a test to have many threads concurrently traversing the same IB, some with ConstIterator, some with Iterator but only reading, and some with Iterator and writing. I have not been able to trigger any failures or crashes. It seems fine. (I did use the mutex a little more aggressively than before.) Why is this not as dangerous as I thought? Mainly because once an iterator (of either variety) thinks it's dealing with an IC-backed ImageBuf, basically, it will just happily keep using the IC all on its own, even if the IB that it's supposedly traversing has, because another thread's iterator has written to a pixel, "localized" the image and severed the IC dependence. Then if it also needs to write, it will (safely, because of a mutex) see that the image is already localized/writable, and start using that representation. So herein lies a very important caveat about using IB iterators: If you have multiple iterators traversing the same IB and any of them are *writing* to the image, the iterators may not see a globally temporally consistent version of the image. Put in plain English: iterators that are strictly reading may see an IC-backed disk image as it was on disk, and not as it has since been modified by a different active iterator. So if you are modifying an image in place, beware of using multiple iterators, because a reading iterator may or may not see the changes that a writing iterator made to a pixel value. Signed-off-by: Larry Gritz <lg@larrygritz.com> Signed-off-by: Peter Kovář <peter.kovar@reflexion.tv>
…oundation#4075) Please look this over. If you are one of the duplicated names and I haven't chosen your preferred canonical email (I made my best guess), please comment here with corrections. Signed-off-by: Larry Gritz <lg@larrygritz.com> Signed-off-by: Peter Kovář <peter.kovar@reflexion.tv>
…twareFoundation#4085) Fix for AcademySoftwareFoundation#4084. Will now check if the given filename exists before checking for REST parameters. Before: ``` bin % ./oiiotool \?.png -o \?-output.png oiiotool ERROR: read : "?.png": ImageInput::create() called with malformed filename Full command line was: > oiiotool ?.png -o ?-output.png ``` After: ``` bin % ./oiiotool \?.png -o \?-output.png ``` No output, `?-output.png` created. Signed-off-by: AdamMainsTL <adam.mains@topazlabs.com> Signed-off-by: Peter Kovář <peter.kovar@reflexion.tv>
## Description Minimal change to update link to OpenEXR's new Test Images page. I poked around a bit to find a "friendlier" variant of the link but could find none. Note that the section containing this link implies that folks need to download these files themselves and should be "placed in a sibling directory to oiio-testimages". The problem is that not even OpenEXR lists the repository for the test images nor does the page linked above offer a download. Should we list out the openexr-images repo address too and say to just clone that? Fixes AcademySoftwareFoundation#4042 --------- Signed-off-by: Jesse Yurkovich <jesse.y@gmail.com> Signed-off-by: Peter Kovář <peter.kovar@reflexion.tv>
Can you clarify what "streaming" means in this context? |
src/jxl.imageio/jxlinput.cpp
Outdated
public: | ||
JxlInput() { init(); } | ||
~JxlInput() override { close(); } | ||
const char* format_name(void) const override { return "jpeg xl"; } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No space, please. People might use the format_name to construct filenames or other uses where spaces will not be good. Let's call it "jpegxl".
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Understood. I mentioned non breaking space instead. However I am OK with jpegxl name. Will modify ASAP.
I strongly recommend
and doing other fixups necessary (CMakeLists.txt, the stuff in imgeioplugins.cpp, etc.). We want
to match exactly. The reason I want you to do this in time for the initial merge is that if we do the "git mv" later, I think it won't transfer the history and attribution properly, it will look like a new file is created and a git log or git blame won't necessarily show your contributions properly (I think). You also need to fix imageinout_test to exclude this format from the test, for now. |
Signed-off-by: Peter Kovář <peter.kovar@reflexion.tv>
We're very close! Let's fix the directory name to match the format_name, and then I think we have everything we need for an initial merge (with lots of improvements to make later, a piece at a time is fine). |
Signed-off-by: Peter Kovář <peter.kovar@reflexion.tv>
Renamed. L👀king at the test suite now. |
I am getting this weird error in the test. jpegxl (jxl): |
Yeah, that's the failure we've been seeing in CI with the imageinout_test. Like I said, I think it's acceptable for right now to disable this new format from that test, so we can get an initial merge with everything you've done so far, and then come back with all the little fixes including whatever is necessary to restore this test. In effect, I want to checkpoint what you've got so far, so that every additional change can be viewed individually as a small patch to consider, rather than continuing to pile things onto this one, which means we need to look at your whole patch again every time there's a modification. Let's accept all the parts that seem good so far and iterate. |
Signed-off-by: Peter Kovář <peter.kovar@reflexion.tv>
With the debug build of JPEG XL library latest modification revealed error in the writer. m_filename = imageinout_test-jpegxl.jxl |
src/jpegxl.imageio/jxloutput.cpp
Outdated
@@ -263,7 +263,8 @@ JxlOutput::save_image() | |||
size); | |||
DBG std::cout << "status = " << status << "\n"; | |||
if (status != JXL_ENC_SUCCESS) { | |||
DBG std::cout << "JxlEncoderAddImageFrame failed.\n"; | |||
JxlEncoderError err = JxlEncoderGetError(m_encoder.get()); | |||
DBG std::cout << "JxlEncoderAddImageFrame failed with error " << err << "\n"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't forget a call to errorfmt() here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks. I guess 4 channel image, i.e. RGB+α is not properly handled yet. Will fix ASAP and try w/ Blender.
Apologies if this isn't exactly the right time or place to ask this, But are there any plans to support depth/z channels and maybe even thermal channels? (could possibly be in a later pull request since this one is just "Initial" support?). This would be very useful for projects like Blender to have depth/z maps/data stored/saved in rendered images for example. libjxl does have support for handling these channel types and there's even a JXL file with such channels here as an example: https://saklistudio.com/jxltests/suz-multichannel-800px.jxl (RGB + Alpha (empty) + Depth + Thermal) I'm really not too familiar with OpenImageIO as a whole so maybe this would be out of scope? although it seems like the EXR support/code has depth/z channel handling in some form via |
OpenImageIO can deal with any number of channels having any names. So yes, totally within scope and nothing special at all. z_channel and alpha_channel are just a special designations in the ImageSpec to mark it for ease of clients of the library so you don't need to go searching for them every time. But it's in no way implies that those are the only channels you can have. |
Signed-off-by: Peter Kovář <peter.kovar@reflexion.tv>
Signed-off-by: Peter Kovář <peter.kovar@reflexion.tv>
JPEG XL export to RGB with α channel tested by Blender. |
@@ -368,6 +368,18 @@ test_all_formats() | |||
OIIO_CHECK_ASSERT(ok && "Failed read/write comparison"); | |||
if (ok) | |||
std::cout << term.ansi("green", "OK\n"); | |||
else { | |||
const float* read_pixels = (const float*)pixels.data(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's at least add a comment here saying that since checked_read reads the data as float values, it's safe to cast this to float*
.
size_t n = pixels.size() / sizeof(float); | ||
float sum_of_absolute_differences = 0.0; | ||
|
||
for (size_t i = 0; i < n; i++) { | ||
float difference = read_pixels[i] - orig_pixels[i]; | ||
sum_of_absolute_differences += fabs(difference); | ||
std::cout << "["<< i << "] " << orig_pixels[i] << " " << read_pixels[i] << " δ " << difference << "\n"; | ||
} | ||
std::cout << "sum of absolute differences " << sum_of_absolute_differences << "\n"; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the section that's failing the clang-format check.
{ | ||
return (feature == "alpha" || feature == "nchannels" | ||
|| feature == "exif" || feature == "ioproxy" | ||
|| feature == "tiles"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that since this is buffering all output until the end, it can support "random_access" also because it doesn't care what order you send the scanlines.
Now that we pass the imageinout_test, I believe that we are ready for a merge if you fix the clang format issue! Then we can come back to beef up this format with other improvements. |
Signed-off-by: Larry Gritz <lg@larrygritz.com>
Signed-off-by: Larry Gritz <lg@larrygritz.com>
Signed-off-by: Larry Gritz <lg@larrygritz.com>
I pushed a few other minor changes. Tests pass now. Mergins. |
@1div0 Don't forget we have some important outstanding issues, including
|
@1div0 I can probably add support for UINT8, UINT16, and FLOAT16 output. But I want to be sure we are not referring to each other. |
@ssh4net OK, I can add EXIF. |
Description
This draft PR is intended for adding JPEG XL support.
Tests
Checklist:
(adding new test cases if necessary).
corresponding Python bindings (and if altering ImageBufAlgo functions, also
exposed the new functionality as oiiotool options).
already run clang-format before submitting, I definitely will look at the CI
test that runs clang-format and fix anything that it highlights as being
nonconforming.