Skip to content

Commit

Permalink
Tonemap improvements
Browse files Browse the repository at this point in the history
  • Loading branch information
awxkee committed Jan 10, 2025
1 parent 726d4d2 commit d4d0ca7
Show file tree
Hide file tree
Showing 8 changed files with 95 additions and 29 deletions.
51 changes: 47 additions & 4 deletions app/src/main/java/com/awxkee/jxlcoder/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,9 @@ class MainActivity : ComponentActivity() {

var assets =
(this@MainActivity.assets.list("") ?: return@launch).toList()
assets = assets.filter { it.contains("20181110_213419__MMC1561-HDR.jxl") }
// assets = assets.filter { it.contains("20181110_213419__MMC1561-HDR.jxl") }
// assets = assets.take(15)
assets = assets.filter { it.contains("cow_image_jxl.jxl") }
for (asset in assets) {
try {
val buffer4 =
Expand All @@ -215,21 +217,62 @@ class MainActivity : ComponentActivity() {

val largeImageSize = JxlCoder.getSize(buffer4)
if (largeImageSize != null) {
val decodingTime = measureTimeMillis {
val srcImage = JxlCoder.decode(
buffer4,
preferredColorConfig = PreferredColorConfig.HARDWARE,
com.awxkee.jxlcoder.ScaleMode.FIT,
toneMapper = JxlToneMapper.REC2408,
)
}
Log.d("JXLMain", "Decoding time ${decodingTime}ms")
val time = measureTimeMillis {
val srcImage = JxlCoder.decodeSampled(
// val srcImage = JxlCoder.decodeSampled(
// buffer4,
// largeImageSize.width / 2,
// largeImageSize.height / 2,
// preferredColorConfig = PreferredColorConfig.HARDWARE,
// com.awxkee.jxlcoder.ScaleMode.FIT,
// toneMapper = JxlToneMapper.REC2408,
// )
val srcImage = JxlCoder.decode(
buffer4,
-1,
largeImageSize.height / 2,
preferredColorConfig = PreferredColorConfig.HARDWARE,
com.awxkee.jxlcoder.ScaleMode.FIT,
toneMapper = JxlToneMapper.REC2408,
)
val fosRec2408 = FileOutputStream(File(cacheDir, File(asset).nameWithoutExtension + ".jpg"))
fosRec2408.use {
srcImage.compress(Bitmap.CompressFormat.JPEG, 90, it)
it.flush()
}
// val srcPerceptual = JxlCoder.decodeSampled(
// buffer4,
// largeImageSize.width / 2,
// largeImageSize.height / 2,
// preferredColorConfig = PreferredColorConfig.HARDWARE,
// com.awxkee.jxlcoder.ScaleMode.FIT,
// toneMapper = JxlToneMapper.REC2408_PERCEPTUAL,
// )
val srcPerceptual = JxlCoder.decode(
buffer4,
preferredColorConfig = PreferredColorConfig.HARDWARE,
com.awxkee.jxlcoder.ScaleMode.FIT,
toneMapper = JxlToneMapper.REC2408_PERCEPTUAL,
)
val fosRec2408Perc = FileOutputStream(File(cacheDir, File(asset).nameWithoutExtension + "_perceptual.jpg"))
fosRec2408Perc.use {
srcPerceptual.compress(Bitmap.CompressFormat.JPEG, 90, it)
it.flush()
}
// val srcImage = JxlCoder.decode(buffer4,
// preferredColorConfig = PreferredColorConfig.RGB_565,
// toneMapper = JxlToneMapper.REC2408)
lifecycleScope.launch {
imagesArray.add(srcImage)
imagesArray.add(srcPerceptual)
}

}
Log.d("JXLCoder", "Decoding done in ${time}ms")
}
Expand Down
4 changes: 2 additions & 2 deletions build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
plugins {
id("com.android.application") version "8.8.0-rc02" apply false
id("com.android.application") version "8.8.0" apply false
id("org.jetbrains.kotlin.android") version "2.0.20" apply false
id("com.android.library") version "8.8.0-rc02" apply false
id("com.android.library") version "8.8.0" apply false
id("com.google.devtools.ksp") version "2.0.20-1.0.25" apply false
}
2 changes: 1 addition & 1 deletion jxlcoder/src/main/cpp/JXLConventions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ Java_com_awxkee_jxlcoder_JxlCoder_gif2JXLImpl(JNIEnv *env, jobject thiz, jbyteAr
for (const EasyGifReader::Frame &frame : gifReader) {
const std::uint32_t *framePixels = frame.pixels();
int frameDuration = frame.duration().milliseconds();
const uint8_t *mSource = reinterpret_cast<const uint8_t *>(framePixels);
const auto *mSource = reinterpret_cast<const uint8_t *>(framePixels);
std::copy(mSource, mSource + frameSize, mPixelStore.begin());
encoder.addFrame(mPixelStore, frameDuration);
}
Expand Down
8 changes: 4 additions & 4 deletions jxlcoder/src/main/cpp/colorspaces/ColorMatrix.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -77,8 +77,8 @@ void applyColorMatrix(uint8_t *inPlace, uint32_t stride, uint32_t width, uint32_
}

float mCoeffs[3] = {coeffs.kr, coeffs.kg, coeffs.kb};
if (toneMapper == CurveToneMapper::REC2408) {
Rec2408ToneMapper mToneMapper(contentBrightness, 250.f, 203.f, mCoeffs);
if (toneMapper == CurveToneMapper::REC2408 || toneMapper == CurveToneMapper::REC2408_PERCEPTUAL) {
Rec2408ToneMapper mToneMapper(contentBrightness, 250.f, 203.f, mCoeffs, toneMapper == CurveToneMapper::REC2408_PERCEPTUAL);
mToneMapper.transferTone(rowVector.data(), width);
} else if (toneMapper == CurveToneMapper::LOGARITHMIC) {
LogarithmicToneMapper mToneMapper(mCoeffs);
Expand Down Expand Up @@ -185,8 +185,8 @@ void applyColorMatrix16Bit(uint16_t *inPlace,
}

float mCoeffs[3] = {coeffs.kr, coeffs.kg, coeffs.kb};
if (toneMapper == CurveToneMapper::REC2408) {
Rec2408ToneMapper mToneMapper(contentBrightness, 250.f, 203.f, mCoeffs);
if (toneMapper == CurveToneMapper::REC2408 || toneMapper == CurveToneMapper::REC2408_PERCEPTUAL) {
Rec2408ToneMapper mToneMapper(contentBrightness, 250.f, 203.f, mCoeffs, toneMapper == CurveToneMapper::REC2408_PERCEPTUAL);
mToneMapper.transferTone(rowVector.data(), width);
} else if (toneMapper == CurveToneMapper::LOGARITHMIC) {
LogarithmicToneMapper mToneMapper(mCoeffs);
Expand Down
52 changes: 37 additions & 15 deletions jxlcoder/src/main/cpp/colorspaces/Rec2408ToneMapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -79,22 +79,44 @@ float rec2408_pq(float intensity, const float intensity_target) {
}

void Rec2408ToneMapper::transferTone(float *inPlace, uint32_t width) const {
float *targetPlace = inPlace;
if (this->perceptual) {
float *targetPlace = inPlace;

const float vWeightA = this->weightA;
const float vWeightB = this->weightB;
const float vWeightA = this->weightA;
const float vWeightB = this->weightB;

for (uint32_t x = 0; x < width; ++x) {
float r = targetPlace[0];
float g = targetPlace[1];
float b = targetPlace[2];
coder::Oklab oklab = coder::Oklab::fromLinearRGB(r, g, b);
float shScale = (1.f + vWeightA * oklab.L) / (1.f + vWeightB * oklab.L);
oklab.L = oklab.L * shScale;
coder::Rgb linearRgb = oklab.toLinearRGB();
targetPlace[0] = std::min(linearRgb.r, 1.f);
targetPlace[1] = std::min(linearRgb.g, 1.f);
targetPlace[2] = std::min(linearRgb.b, 1.f);
targetPlace += 3;
for (uint32_t x = 0; x < width; ++x) {
float r = targetPlace[0];
float g = targetPlace[1];
float b = targetPlace[2];
coder::Oklab oklab = coder::Oklab::fromLinearRGB(r, g, b);
float shScale = (1.f + vWeightA * oklab.L) / (1.f + vWeightB * oklab.L);
oklab.L = oklab.L * shScale;
coder::Rgb linearRgb = oklab.toLinearRGB();
targetPlace[0] = std::min(linearRgb.r, 1.f);
targetPlace[1] = std::min(linearRgb.g, 1.f);
targetPlace[2] = std::min(linearRgb.b, 1.f);
targetPlace += 3;
}
} else {
float *targetPlace = inPlace;

const float vWeightA = this->weightA;
const float vWeightB = this->weightB;

for (uint32_t x = 0; x < width; ++x) {
float r = targetPlace[0];
float g = targetPlace[1];
float b = targetPlace[2];
float inLight = 0.2627f * static_cast<float>(r) + 0.6780f * static_cast<float>(g) + 0.0593f * static_cast<float>(b);
if (inLight == 0) {
continue;
}
float scale = (1.f + vWeightA * inLight) / (1.f + vWeightB * inLight);
targetPlace[0] = std::min(r * scale, 1.f);
targetPlace[1] = std::min(g * scale, 1.f);
targetPlace[2] = std::min(b * scale, 1.f);
targetPlace += 3;
}
}
}
3 changes: 2 additions & 1 deletion jxlcoder/src/main/cpp/colorspaces/Rec2408ToneMapper.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ class Rec2408ToneMapper {
Rec2408ToneMapper(const float contentMaxBrightness,
const float displayMaxBrightness,
const float whitePoint,
const float primaries[3]) {
const float primaries[3], bool perceptual): perceptual(perceptual) {
std::copy(primaries, primaries + 3, lumaPrimaries);

this->Ld = contentMaxBrightness / whitePoint;
Expand All @@ -49,6 +49,7 @@ class Rec2408ToneMapper {
private:
float lumaPrimaries[3] = {0};
float Ld = 0;
bool perceptual;
float weightA = 0;
float weightB = 0;
};
Expand Down
2 changes: 1 addition & 1 deletion jxlcoder/src/main/cpp/colorspaces/ToneMapper.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
#define AVIF_TONEMAPPER_H

enum CurveToneMapper {
REC2408 = 1, LOGARITHMIC = 2, FILMIC = 3, ACES = 4, TONE_SKIP = 5
REC2408 = 1, LOGARITHMIC = 2, FILMIC = 3, ACES = 4, REC2408_PERCEPTUAL = 5, TONE_SKIP = 6
};

#endif //AVIF_TONEMAPPER_H
Original file line number Diff line number Diff line change
Expand Up @@ -29,5 +29,5 @@
package com.awxkee.jxlcoder

enum class JxlToneMapper(val value: Int) {
REC2408(1), LOGARITHMIC(2), FILMIC(3), ACES(4)
REC2408(1), LOGARITHMIC(2), FILMIC(3), ACES(4), REC2408_PERCEPTUAL(5),
}

0 comments on commit d4d0ca7

Please sign in to comment.