Inflation from PNG to EXR, EXR to EXR no

Little interesting discovery, so im trying to optimize some textures from Evermotion, they come in PNG, so i decided lets export them as .exr for HDR purpose testing, okay the source file is 10MB (PNG), when EXR converted using NVIDIA Texture tools, it goes from 10MB to 26MB with mipmap disabled but in the app it reports 8.39MB.

when using exr from substance painter the file is 68MB and if i use NVIDIA Texture tools to convert them to EXR again using the same settings as before BC1, on exr, it goes from 68MB to 12MB, so something is being funky.

not sure what the issue could be.


exr to exr using NVTT

Hi @AetherCLion! Would it be possible to send me the original .png file, either in a DM here or at nbickford@nvidia.com? I’ve got a guess it could be if the original .png is 16-bit or if Substance uses a different lossless compression codec, but would like to test out my guess.

Note that BC1 is a lossy compression algorithm for LDR textures, like a fixed-bitrate version of JPEG; it compresses textures to 4 bits per pixel, but removes detail in the process, and can’t store colors brighter than 1.0 or colors darker than 0.0. The “16x4f” RGBA format is an uncompressed HDR format that can* store textures from EXR files without quality loss (aside from floating-point error if there’s a color space transform that occurs). For lossy DDS compression of HDR images, I usually recommend BC6U.

*Correction (2024-08-16): I didn’t realize until now that EXR files can contain 32-bit floating-point data, instead of 16-bit floating-point data (which is the default)! Canine_ForSubstance…exr turns out to be a 32-bit floating-point EXR file.

Thanks!

Sent in DMs, let me know if the link works

1 Like


new testing, source 2 PNGs 93MB and 28MB, im using nvcompress via custom C# code based on the documentation and the output files are big as well both png to EXR is 87MB, Im using BC6S for nvcompress, then using NVTT via C# code to optimize them with BC6S again intaking the compress exr, optimize them using nvtt and the files are BIGGER? 161MB and 216MB


im very clueless what is making it go from one size to another

Hi @AetherCLion! I think I’ve got an idea of what’s going on here.

For the first message:

  • AM227_001_Diffuse_PTSH-min is a PNG file that uses an 8-bit-per-pixel palette. Because PNG has lossless compression, it only uses 10,823,121 bytes for 16,777,216 pixels.
    • Because lossless compression is variable-rate, it’s very difficult to calculate the compressed size of an image without compressing it. For instance, a texture of white noise will create a large PNG or EXR file, while a texture with a single color will create a small PNG or EXR file.
  • The Exporter saves EXR files using EXR’s default format, which is 16-bit-per-component floating-point HDR with lossless PIZ compression. This would be 100,663,296 bytes uncompressed, but PIZ compression brings it down to 26,953,689 bytes. (It’s larger than the original, but that’s because EXR can store more values; while PIZ compression takes advantage of how the data only has a limited number of values, it doesn’t do as well as PNG’s compression here).
  • Substance Painter saves EXR files using 32-bit-per-component floating-point HDR (which I didn’t realize was possible until now)! Because OpenEXR’s lossless compression doesn’t perform as well on 32-bpc (bits per component) floating-point as on 16-bpc floating-point, it’s more than twice the size of the 16-bpc EXR file the Exporter created.
  • The box in the lower-right in the Exporter reports the data size if the current file was saved as a DDS file (or KTX2 when Basis is selected). Other file types might have different sizes, but we display the DDS file size because this tool’s usually used to create DDS files, and because we don’t know which file type the user will save to until they click “Save As…”. In this case, BC1 uses 4 bits per pixel, so it uses 4096 * 4096 * (4 bits/8 bits per byte) = 8,366,608 bytes of data.

In short, the file sizes come from the formats they use:

  • -min_dds is a DDS file that uses BC1 (4 bits per pixel)
  • -min is a PNG file that uses an 8-bit palette (256 colors, 8 bits per pixel + lossless compression)
  • -min_EXR and min_EXR-BC1 are EXR files that use 16-bpc floating-point (48 bits per pixel + lossless compression)
  • the Substance Painter file is an EXR file that uses 32-bpc floating-point (96 bits per pixel + lossless compression)

For the second message, the NVTT EXR file is smaller because it quantized all the colors in the EXR file from 32-bit-per-channel floating-point to 16-bit-per-channel floating point; because 16bpc floating-point compresses better, it’s more than twice as small.

  • There’s a really good idea for a feature here: maybe the Exporter should support saving EXR files as 32-bit! In the meantime, I think the formats the Exporter can write to that support 32-bit floating-point are 32x4f KTX2 (which will probably be the smallest since it has Zstandard lossless compression by default), .pfm (no support for alpha, 96 bits per pixel), and 32x4f DDS (128 bits per pixel).

For the third and fourth messages, this behavior is a bit counterintuitive, but it comes down to how BC6S works and how OpenEXR’s PIZ compression works! The short answer is that the input contains only 256 unique values; PIZ compression recognizes that to some extent and compresses the input well. Lossy compression codecs usually reduce entropy, but this is a case where BC6S encodes some pixels differently depending on other pixels in the block – so the output actually has more distinct values, and PIZ compression performs worse.

Here’s the longer explanation! To help visualize what’s going on, here I’ve displayed the hex contents of two DDS files. The color data for each one starts at offset 0x80, and stores the 16-bit floating-point luminance of each pixel in little-endian (least significant byte first). The one on the left is from a grayscale PNG file converted to EXR then to 16f DDS, and the one on the right is from that same file compressed to BC6S DDS and then to 16f DDS.

  • The inputs are PGM files, probably using 1 byte per pixel, and storing only luminance. So there’s 256 different values for pixels.
  • The 16-bit numbers we see on the left don’t look like round numbers, but there are only 256 different 16-bit numbers in the file on the left.
    • The reason why is because EXR and 16f store linear colors. To convert from sRGB 0-255 to a 16-bit floating-point number, we divide by 255 and then apply the sRGB-to-linear transfer function. So the 16-bit numbers we see don’t look round (for instance, 0x1ce4 = 0.0047760009765625, which isn’t a multiple of 1/255; converting it back from linear to sRGB, we get 0.0588…, which is 15/255.
    • Because there’s only 256 different 16-bit numbers, PIZ compression does relatively OK on this file.
  • BC6S compression compresses each 4x4 pixel block as well as it can. But because it’s designed for positive and negative HDR RGB content, it uses a block compression algorithm that’s meant for that (instead of realizing that the input is really an 8-bit grayscale image converted to linear). Because of this block compression, we sometimes get blocks like the one stored in bytes 0x260-0x26F; the block contains multiple shades of gray, so the pixels stored in the bytes I’ve highlighted change.
    • This means that the output file actually has more unique 16-bit colors, and so EXR’s PIZ compression performs much worse on it (specifically, its entropy coding performs worse because there are more distinct values)!

An analogy is that this is sort of like taking a PNG image, saving it as a JPEG image, and then saving it as a PNG image again. This test PNG file is 1,113 bytes (it compresses really well because it has lots of empty space and solid colors):

test-image

Here I’ve converted it to a 20,141 byte JPEG file using Photoshop quality 6:

test-image.png

Converted back to a PNG file, it uses 18,420 bytes, since JPEG compression introduced some artifacts that PNG spends extra space losslessly encoding:

test-image.png.jpg

Hopefully this answers your questions about why we sometimes see these effects when converting back and forth between different lossy and lossless formats.

I think the next question would be – what file types do you ultimately need to convert to for your app? I can probably recommend the best format to use for those.

Thanks!

So my app is soon to be open source, since it would leverage nvtt_exporter.exe within a standalone app or within unity, to convert textures to a more VRAM efficient format,

i would like to convert PNGs to EXR for the VRAM benefits, based on my research here @ x.com

  1. Select Texture Format:
  • BC1: Best for color maps without alpha, typically used for .DDS files.
  • BC3: Best for color maps with alpha, typically used for .DDS files.
  • BC6S: Best for HDR images, typically used for .EXR or .TIFF files.
  • BC7: Best for high-quality color maps with or without alpha, typically used for .DDS files.
  1. Select the Image Type:
  • 1. Color Map
  • 2. Normal MapTooltip: BC6S format is best suited for color maps.
  1. Select the Compression Quality:
  • 1. Fastest
  • 2. Normal
  • 3. Production
  • 4. Highest
  1. Compression Decision:
  • Would you like to compress the files before optimization? (Y/N)

Optimization Phase

After the initial compression, the app proceeds to the optimization phase where users can:

  1. Select Texture Format to Save As:
  • DDS, EXR, HDR, TIFF
  1. Select the Compression Quality:
  • 1. Fastest
  • 2. Normal
  • 3. Production
  • 4. Highest

After these selections, the app processes the files accordingly.

In summary, this app is designed to take PNGs (e.g., from Evermotion), convert them to EXR format for maximum VRAM benefits, and use them for building worlds in VR Chat and Unity.

as u seen with the substance export with NVTT i was able to save 82.35% 68MB EXR to 12MB EXR using NVTT.

just to ensure we are on the same page, in a nutshell the inflated EXR sizes were due to the way EXR files handle compression. When converting from PNG to EXR, certain EXR compression methods might not be as efficient for specific types of data, leading to larger file sizes. Additionally, the internal handling of channels and metadata in EXR can contribute to size inflation. Proper selection of compression settings and understanding the data characteristics can help mitigate this issue?

There are a few features I would like to see in the next update,

  • Ability to change exr or tiff or hdr export bits (16/32)
  • Ability to tweak compression settings for EXR/TIFF/HDR (as u where able to seen with substance to nvtt_exporter)

Extended Compression Options for EXR Files

  • Feature: Introduce more advanced compression methods specifically for EXR files, including support for DWAA/DWAB, B44/B44A, RLE, Zip (Single Scanline), Zip (Multi-Scanline Blocks), PXR24 and PIZ compression types, with options to fine-tune these settings.
  • Benefit: Provides users with greater control over the compression of EXR files, allowing them to balance file size and quality more effectively, particularly in scenarios involving HDR content.
  1. RLE (Run Length Encoding):
  • Description: A simple form of compression that is lossless and works well with images containing large areas of a single color.
  • Use Case: Effective for images with large uniform areas, though not very efficient for detailed or noisy images.
  1. ZIP (Single Scanline):
  • Description: Uses the deflate algorithm to compress each scanline of the image independently.
  • Use Case: Best for images with subtle gradients; a good balance between compression speed and ratio.
  1. ZIP (Multi-Scanline Blocks):
  • Description: Compresses multiple scanlines together (typically 16 scanlines per block) using the ZIP compression.
  • Use Case: Similar to the single scanline ZIP compression but slightly more efficient on images with regular patterns.
  1. PIZ (Wavelet Compression):
  • Description: Lossless compression using a wavelet-based algorithm; very effective on grainy or noisy images.
  • Use Case: Ideal for images with high-frequency detail or film grain, as it compresses well without loss of data.
  1. PXR24:
  • Description: Lossy 24-bit float compression that converts 32-bit float data to 24-bit float for compression.
  • Use Case: Suitable for images where some loss of precision is acceptable, such as in preview images or intermediate renders.
  1. B44:
  • Description: Lossy compression method that compresses four scanlines together, mainly used for half-float (16-bit) images.
  • Use Case: Used in situations where reduced precision is acceptable, like in intermediate steps or when targeting hardware with limited precision.
  1. B44A:
  • Description: An improved version of B44 compression that includes better handling of areas with flat color.
  • Use Case: Useful for images with large uniform areas or gradients, offering a better compression ratio while still being lossy.
  1. DWAA (DWA Compression - Version A):
  • Description: A lossy, perceptual compression method similar to JPEG, designed by DreamWorks Animation.
  • Use Case: Ideal for animation and visual effects where a slight loss in quality is tolerable in exchange for smaller file sizes.
  1. DWAB (DWA Compression - Version B):
  • Description: Similar to DWAA but with a different rate of compression, offering a slightly different quality-to-compression ratio.
  • Use Case: Best suited for sequences where consistency in quality is critical, and the specific compression ratio of DWAB is preferred.

Quality Preview and Artifact Analysis

  • Feature: Introduce a feature that provides real-time previews of textures after compression, including a detailed analysis of potential artifacts. This could include heatmaps or side-by-side comparisons showing the differences between the original and compressed textures.
  • Benefit: Helps users better understand the impact of compression on texture quality and make more informed decisions about which settings to use.

Custom Metadata Handling for EXR Files

  • Feature: Allow users to manage and customize the metadata in EXR files during the compression process, including stripping unnecessary metadata to reduce file size or optimizing channel handling.
  • Benefit: Enables more efficient file handling, particularly in workflows where file size is critical, such as in VR or mobile applications.

i found a tool to compress EXR files called PYCO ColorSpace Converter
i took this file “light_new_horizontal_wooden_logs_56_07_diffuse_ACEScg-JPG TO EXR DWAB 100Q 32F”
import into NVIDIA Texture Tools, exported as wooden_logs_diffuse_ACEScg-JPG TO EXR DWAB 100Q 32F_NVTT-EXPORTER-BC6S

End result intake file is 73MB, export file is 204MB both are EXR

just did a test with BC1 and both export files are 204MB with highest compression, even though with PYCO i was able to compress them as u can see in the image

So my question that boggles my mind is why is Nvidia texture tools for what ever reason taking a 8k png to exr gets bigger, import that exr file, and export it again as default bc6s for hdr file size doubles haha.

BUT this reacts differently if the PNG is exported from substance as PNG 16 Bits 8K we get a different result


From PNG 16 Bit to EXR BC7/BC6 file size is reduced
exported from substance painter, i think to summarize this, is that using NVIDIA Texture Tools, there is no way to specify how to export it, no bits weather 16 or 32f, hence my theory of inflation if the texture was exported in a different way as seen with my first post wooden_logs_56_07_diffuse file size 93MB
Using NVTT File size 146. using PYCO to compress them using DWAB 16 Bits and 32 bits we get 82MB.

im confusing myself here trying to understand why NVTT is being weird, sorry for the conflusion!

So even does it with JPG to PNG using BC7 Compression with MipMaps turned off?