Image: Add Quality setting to EXR DWAA/DWAB compression
EXR DWAA and DWAB are conceptually similar to lossy JPG compression, with a tunable file size vs image quality parameter. However, previously Blender always used the fixed default setting, which is kinda similar to very high quality (like 97) for JPG. Internally EXR DWA/DWB quality parameter is inverted scale, i.e. 0 is best/lossless quality, and increased setting value means decreased quality. However the rest of Blender UI uses 1-100 JPG-like quality scale, where values above 90 are "visually lossless", 100 is lossless, and going below something like 50 would be visually quite lossy. So map that to internal DWA setting: - blender 100 -> DWA 0 - blender 97 -> DWA 45 The rest is linear relation based on those two points. Pull Request: https://projects.blender.org/blender/blender/pulls/128790
This commit is contained in:
committed by
Aras Pranckevicius
parent
4f5f8f6e17
commit
8b275092e0
@@ -118,6 +118,7 @@ int BKE_imtype_to_ftype(const char imtype, ImbFormatOptions *r_options)
|
||||
return IMB_FTYPE_TIF;
|
||||
}
|
||||
if (ELEM(imtype, R_IMF_IMTYPE_OPENEXR, R_IMF_IMTYPE_MULTILAYER)) {
|
||||
r_options->quality = 90;
|
||||
return IMB_FTYPE_OPENEXR;
|
||||
}
|
||||
#ifdef WITH_CINEON
|
||||
@@ -649,6 +650,7 @@ void BKE_image_format_to_imbuf(ImBuf *ibuf, const ImageFormatData *imf)
|
||||
ibuf->foptions.flag |= OPENEXR_HALF;
|
||||
}
|
||||
ibuf->foptions.flag |= (imf->exr_codec & OPENEXR_COMPRESS);
|
||||
ibuf->foptions.quality = quality;
|
||||
}
|
||||
#endif
|
||||
#ifdef WITH_CINEON
|
||||
|
||||
@@ -998,9 +998,10 @@ bool BKE_image_render_write_exr(ReportList *reports,
|
||||
|
||||
BLI_file_ensure_parent_dir_exists(filepath);
|
||||
|
||||
int compress = (imf ? imf->exr_codec : 0);
|
||||
const int compress = (imf ? imf->exr_codec : 0);
|
||||
const int quality = (imf ? imf->quality : 90);
|
||||
bool success = IMB_exr_begin_write(
|
||||
exrhandle, filepath, rr->rectx, rr->recty, compress, rr->stamp_data);
|
||||
exrhandle, filepath, rr->rectx, rr->recty, compress, quality, rr->stamp_data);
|
||||
if (success) {
|
||||
IMB_exr_write_channels(exrhandle);
|
||||
}
|
||||
|
||||
@@ -986,6 +986,9 @@ void uiTemplateImageSettings(uiLayout *layout, PointerRNA *imfptr, bool color_ma
|
||||
|
||||
if (ELEM(imf->imtype, R_IMF_IMTYPE_OPENEXR, R_IMF_IMTYPE_MULTILAYER)) {
|
||||
uiItemR(col, imfptr, "exr_codec", UI_ITEM_NONE, nullptr, ICON_NONE);
|
||||
if (ELEM(imf->exr_codec & OPENEXR_COMPRESS, R_IMF_EXR_CODEC_DWAA, R_IMF_EXR_CODEC_DWAB)) {
|
||||
uiItemR(col, imfptr, "quality", UI_ITEM_NONE, nullptr, ICON_NONE);
|
||||
}
|
||||
}
|
||||
|
||||
if (is_render_out && ELEM(imf->imtype, R_IMF_IMTYPE_OPENEXR, R_IMF_IMTYPE_MULTILAYER)) {
|
||||
|
||||
@@ -50,6 +50,7 @@ bool IMB_exr_begin_write(void *handle,
|
||||
int width,
|
||||
int height,
|
||||
int compress,
|
||||
int quality,
|
||||
const StampData *stamp);
|
||||
/**
|
||||
* Only used for writing temp. render results (not image files)
|
||||
|
||||
@@ -77,6 +77,7 @@
|
||||
|
||||
#include "BLI_blenlib.h"
|
||||
#include "BLI_fileops.h"
|
||||
#include "BLI_math_base.hh"
|
||||
#include "BLI_math_color.h"
|
||||
#include "BLI_mmap.h"
|
||||
#include "BLI_threads.h"
|
||||
@@ -385,7 +386,20 @@ bool imb_is_a_openexr(const uchar *mem, const size_t size)
|
||||
return Imf::isImfMagic((const char *)mem);
|
||||
}
|
||||
|
||||
static void openexr_header_compression(Header *header, int compression)
|
||||
static int openexr_jpg_like_quality_to_dwa_quality(int q)
|
||||
{
|
||||
q = blender::math::clamp(q, 0, 100);
|
||||
|
||||
/* Map "visually lossless" JPG quality of 97 to default DWA level of 45,
|
||||
* "lossless" JPG quality of 100 to DWA level of 0, and everything else
|
||||
* linearly based on those. */
|
||||
constexpr int x0 = 100, y0 = 0;
|
||||
constexpr int x1 = 97, y1 = 45;
|
||||
q = y0 + (q - x0) * (y1 - y0) / (x1 - x0);
|
||||
return q;
|
||||
}
|
||||
|
||||
static void openexr_header_compression(Header *header, int compression, int quality)
|
||||
{
|
||||
switch (compression) {
|
||||
case R_IMF_EXR_CODEC_NONE:
|
||||
@@ -415,9 +429,11 @@ static void openexr_header_compression(Header *header, int compression)
|
||||
#if OPENEXR_VERSION_MAJOR > 2 || (OPENEXR_VERSION_MAJOR >= 2 && OPENEXR_VERSION_MINOR >= 2)
|
||||
case R_IMF_EXR_CODEC_DWAA:
|
||||
header->compression() = DWAA_COMPRESSION;
|
||||
header->dwaCompressionLevel() = openexr_jpg_like_quality_to_dwa_quality(quality);
|
||||
break;
|
||||
case R_IMF_EXR_CODEC_DWAB:
|
||||
header->compression() = DWAB_COMPRESSION;
|
||||
header->dwaCompressionLevel() = openexr_jpg_like_quality_to_dwa_quality(quality);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
@@ -462,7 +478,8 @@ static bool imb_save_openexr_half(ImBuf *ibuf, const char *filepath, const int f
|
||||
try {
|
||||
Header header(width, height);
|
||||
|
||||
openexr_header_compression(&header, ibuf->foptions.flag & OPENEXR_COMPRESS);
|
||||
openexr_header_compression(
|
||||
&header, ibuf->foptions.flag & OPENEXR_COMPRESS, ibuf->foptions.quality);
|
||||
openexr_header_metadata(&header, ibuf);
|
||||
|
||||
/* create channels */
|
||||
@@ -563,7 +580,8 @@ static bool imb_save_openexr_float(ImBuf *ibuf, const char *filepath, const int
|
||||
try {
|
||||
Header header(width, height);
|
||||
|
||||
openexr_header_compression(&header, ibuf->foptions.flag & OPENEXR_COMPRESS);
|
||||
openexr_header_compression(
|
||||
&header, ibuf->foptions.flag & OPENEXR_COMPRESS, ibuf->foptions.quality);
|
||||
openexr_header_metadata(&header, ibuf);
|
||||
|
||||
/* create channels */
|
||||
@@ -875,6 +893,7 @@ bool IMB_exr_begin_write(void *handle,
|
||||
int width,
|
||||
int height,
|
||||
int compress,
|
||||
int quality,
|
||||
const StampData *stamp)
|
||||
{
|
||||
ExrHandle *data = (ExrHandle *)handle;
|
||||
@@ -889,7 +908,7 @@ bool IMB_exr_begin_write(void *handle,
|
||||
header.channels().insert(echan->name, Channel(echan->use_half_float ? Imf::HALF : Imf::FLOAT));
|
||||
}
|
||||
|
||||
openexr_header_compression(&header, compress);
|
||||
openexr_header_compression(&header, compress, quality);
|
||||
BKE_stamp_info_callback(
|
||||
&header, const_cast<StampData *>(stamp), openexr_header_metadata_callback, false);
|
||||
/* header.lineOrder() = DECREASING_Y; this crashes in windows for file read! */
|
||||
|
||||
@@ -45,7 +45,8 @@ class ImBufTest(AbstractImBufTest):
|
||||
for s in settings:
|
||||
if s == "color_depth":
|
||||
name += str(settings[s]).rjust(2, '0') + "-"
|
||||
else:
|
||||
# do not embed exr quality into test file name
|
||||
elif not(s == "quality" and ext == "exr"):
|
||||
name += str(settings[s]) + "-"
|
||||
|
||||
setattr(image_settings, s, settings[s])
|
||||
@@ -123,17 +124,17 @@ class ImBufSaveTest(ImBufTest):
|
||||
self.skip_if_format_missing("OPENEXR")
|
||||
|
||||
self.check(src="rgba08", ext="exr", settings={"file_format": "OPEN_EXR", "color_mode": "BW", "color_depth": "16", "exr_codec": "ZIP"})
|
||||
self.check(src="rgba08", ext="exr", settings={"file_format": "OPEN_EXR", "color_mode": "RGB", "color_depth": "16", "exr_codec": "DWAA"})
|
||||
self.check(src="rgba08", ext="exr", settings={"file_format": "OPEN_EXR", "color_mode": "RGBA", "color_depth": "16", "exr_codec": "DWAB"})
|
||||
self.check(src="rgba08", ext="exr", settings={"file_format": "OPEN_EXR", "color_mode": "BW", "color_depth": "32", "exr_codec": "DWAB"})
|
||||
self.check(src="rgba08", ext="exr", settings={"file_format": "OPEN_EXR", "color_mode": "RGB", "color_depth": "32", "exr_codec": "DWAA"})
|
||||
self.check(src="rgba08", ext="exr", settings={"file_format": "OPEN_EXR", "color_mode": "RGB", "color_depth": "16", "exr_codec": "DWAA", "quality": 97})
|
||||
self.check(src="rgba08", ext="exr", settings={"file_format": "OPEN_EXR", "color_mode": "RGBA", "color_depth": "16", "exr_codec": "DWAB", "quality": 97})
|
||||
self.check(src="rgba08", ext="exr", settings={"file_format": "OPEN_EXR", "color_mode": "BW", "color_depth": "32", "exr_codec": "DWAB", "quality": 97})
|
||||
self.check(src="rgba08", ext="exr", settings={"file_format": "OPEN_EXR", "color_mode": "RGB", "color_depth": "32", "exr_codec": "DWAA", "quality": 97})
|
||||
self.check(src="rgba08", ext="exr", settings={"file_format": "OPEN_EXR", "color_mode": "RGBA", "color_depth": "32", "exr_codec": "ZIP"})
|
||||
|
||||
self.check(src="rgba32", ext="exr", settings={"file_format": "OPEN_EXR", "color_mode": "BW", "color_depth": "16", "exr_codec": "ZIP"})
|
||||
self.check(src="rgba32", ext="exr", settings={"file_format": "OPEN_EXR", "color_mode": "RGB", "color_depth": "16", "exr_codec": "DWAA"})
|
||||
self.check(src="rgba32", ext="exr", settings={"file_format": "OPEN_EXR", "color_mode": "RGBA", "color_depth": "16", "exr_codec": "DWAB"})
|
||||
self.check(src="rgba32", ext="exr", settings={"file_format": "OPEN_EXR", "color_mode": "BW", "color_depth": "32", "exr_codec": "DWAB"})
|
||||
self.check(src="rgba32", ext="exr", settings={"file_format": "OPEN_EXR", "color_mode": "RGB", "color_depth": "32", "exr_codec": "DWAA"})
|
||||
self.check(src="rgba32", ext="exr", settings={"file_format": "OPEN_EXR", "color_mode": "RGB", "color_depth": "16", "exr_codec": "DWAA", "quality": 97})
|
||||
self.check(src="rgba32", ext="exr", settings={"file_format": "OPEN_EXR", "color_mode": "RGBA", "color_depth": "16", "exr_codec": "DWAB", "quality": 97})
|
||||
self.check(src="rgba32", ext="exr", settings={"file_format": "OPEN_EXR", "color_mode": "BW", "color_depth": "32", "exr_codec": "DWAB", "quality": 97})
|
||||
self.check(src="rgba32", ext="exr", settings={"file_format": "OPEN_EXR", "color_mode": "RGB", "color_depth": "32", "exr_codec": "DWAA", "quality": 97})
|
||||
self.check(src="rgba32", ext="exr", settings={"file_format": "OPEN_EXR", "color_mode": "RGBA", "color_depth": "32", "exr_codec": "ZIP"})
|
||||
|
||||
def test_save_hdr(self):
|
||||
|
||||
Reference in New Issue
Block a user