Skip to content

Commit

Permalink
add support for encoding linear half-float images
Browse files Browse the repository at this point in the history
This changes adds support for encoding linear half float hdr intent
inputs. linear color transfer inputs are restricted to half float color
format only to keep it symmetrical with decoder.

fixes google#279

Test: ./ultrahdr_app
  • Loading branch information
ram-mohan authored and DichenZhang1 committed Oct 31, 2024
1 parent 232bd43 commit 613fee1
Show file tree
Hide file tree
Showing 13 changed files with 334 additions and 72 deletions.
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,10 @@ libultrahdr includes two classes of APIs, one to compress and the other to decom

| Scenario | Hdr intent raw | Sdr intent raw | Sdr intent compressed | Gain map compressed | Quality | Exif | Use Case |
|:---------:| :----------: | :----------: | :---------------------: | :-------------------: | :-------: | :---------: | :-------- |
| API - 0 | P010 or rgb1010102 | No | No | No | Optional| Optional | Used if, only hdr raw intent is present. [^1] |
| API - 1 | P010 or rgb1010102 | YUV420 or rgba8888 | No | No | Optional| Optional | Used if, hdr raw and sdr raw intents are present.[^2] |
| API - 2 | P010 or rgb1010102 | YUV420 or rgba8888 | Yes | No | No | No | Used if, hdr raw, sdr raw and sdr compressed intents are present.[^3] |
| API - 3 | P010 or rgb1010102 | No | Yes | No | No | No | Used if, hdr raw and sdr compressed intents are present.[^4] |
| API - 0 | P010 or rgba1010102 or rgbaf16 | No | No | No | Optional| Optional | Used if, only hdr raw intent is present. [^1] |
| API - 1 | P010 or rgba1010102 or rgbaf16 | YUV420 or rgba8888 | No | No | Optional| Optional | Used if, hdr raw and sdr raw intents are present.[^2] |
| API - 2 | P010 or rgba1010102 or rgbaf16 | YUV420 or rgba8888 | Yes | No | No | No | Used if, hdr raw, sdr raw and sdr compressed intents are present.[^3] |
| API - 3 | P010 or rgba1010102 or rgbaf16 | No | Yes | No | No | No | Used if, hdr raw and sdr compressed intents are present.[^4] |
| API - 4 | No | No | Yes | Yes | No | No | Used if, sdr compressed, gain map compressed and GainMap Metadata are present.[^5] |

[^1]: Tonemap hdr to sdr. Compute gain map from hdr and sdr. Compress sdr and gainmap at quality configured. Add exif if provided. Combine sdr compressed, gainmap in multi picture format with gainmap metadata.
Expand Down
46 changes: 44 additions & 2 deletions examples/ultrahdr_app.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,10 @@ static bool loadFile(const char* filename, uhdr_raw_image_t* handle) {
const int bpp = 4;
READ_BYTES(ifd, handle->planes[UHDR_PLANE_PACKED], handle->w * handle->h * bpp)
return true;
} else if (handle->fmt == UHDR_IMG_FMT_64bppRGBAHalfFloat) {
const int bpp = 8;
READ_BYTES(ifd, handle->planes[UHDR_PLANE_PACKED], handle->w * handle->h * bpp)
return true;
} else if (handle->fmt == UHDR_IMG_FMT_12bppYCbCr420) {
READ_BYTES(ifd, handle->planes[UHDR_PLANE_Y], handle->w * handle->h)
READ_BYTES(ifd, handle->planes[UHDR_PLANE_U], (handle->w / 2) * (handle->h / 2))
Expand Down Expand Up @@ -332,6 +336,10 @@ class UltraHdrAppInput {
free(mRawRgba1010102Image.planes[i]);
mRawRgba1010102Image.planes[i] = nullptr;
}
if (mRawRgbaF16Image.planes[i]) {
free(mRawRgbaF16Image.planes[i]);
mRawRgbaF16Image.planes[i] = nullptr;
}
if (mRawYuv420Image.planes[i]) {
free(mRawYuv420Image.planes[i]);
mRawYuv420Image.planes[i] = nullptr;
Expand All @@ -356,6 +364,7 @@ class UltraHdrAppInput {
bool fillUhdrImageHandle();
bool fillP010ImageHandle();
bool fillRGBA1010102ImageHandle();
bool fillRGBAF16ImageHandle();
bool convertP010ToRGBImage();
bool fillYuv420ImageHandle();
bool fillRGBA8888ImageHandle();
Expand Down Expand Up @@ -406,6 +415,7 @@ class UltraHdrAppInput {

uhdr_raw_image_t mRawP010Image{};
uhdr_raw_image_t mRawRgba1010102Image{};
uhdr_raw_image_t mRawRgbaF16Image{};
uhdr_raw_image_t mRawYuv420Image{};
uhdr_raw_image_t mRawRgba8888Image{};
uhdr_compressed_image_t mSdrIntentCompressedImage{};
Expand Down Expand Up @@ -471,6 +481,23 @@ bool UltraHdrAppInput::fillRGBA1010102ImageHandle() {
return loadFile(mHdrIntentRawFile, &mRawRgba1010102Image);
}

bool UltraHdrAppInput::fillRGBAF16ImageHandle() {
const int bpp = 8;
mRawRgbaF16Image.fmt = UHDR_IMG_FMT_64bppRGBAHalfFloat;
mRawRgbaF16Image.cg = mHdrCg;
mRawRgbaF16Image.ct = mHdrTf;
mRawRgbaF16Image.range = UHDR_CR_FULL_RANGE;
mRawRgbaF16Image.w = mWidth;
mRawRgbaF16Image.h = mHeight;
mRawRgbaF16Image.planes[UHDR_PLANE_PACKED] = malloc(mWidth * mHeight * bpp);
mRawRgbaF16Image.planes[UHDR_PLANE_UV] = nullptr;
mRawRgbaF16Image.planes[UHDR_PLANE_V] = nullptr;
mRawRgbaF16Image.stride[UHDR_PLANE_PACKED] = mWidth;
mRawRgbaF16Image.stride[UHDR_PLANE_UV] = 0;
mRawRgbaF16Image.stride[UHDR_PLANE_V] = 0;
return loadFile(mHdrIntentRawFile, &mRawRgbaF16Image);
}

bool UltraHdrAppInput::fillRGBA8888ImageHandle() {
const int bpp = 4;
mRawRgba8888Image.fmt = UHDR_IMG_FMT_32bppRGBA8888;
Expand Down Expand Up @@ -610,6 +637,11 @@ bool UltraHdrAppInput::encode() {
std::cerr << " failed to load file " << mHdrIntentRawFile << std::endl;
return false;
}
} else if (mHdrCf == UHDR_IMG_FMT_64bppRGBAHalfFloat) {
if (!fillRGBAF16ImageHandle()) {
std::cerr << " failed to load file " << mHdrIntentRawFile << std::endl;
return false;
}
} else {
std::cerr << " invalid hdr intent color format " << mHdrCf << std::endl;
return false;
Expand Down Expand Up @@ -671,6 +703,8 @@ bool UltraHdrAppInput::encode() {
RET_IF_ERR(uhdr_enc_set_raw_image(handle, &mRawP010Image, UHDR_HDR_IMG))
} else if (mHdrCf == UHDR_IMG_FMT_32bppRGBA1010102) {
RET_IF_ERR(uhdr_enc_set_raw_image(handle, &mRawRgba1010102Image, UHDR_HDR_IMG))
} else if (mHdrCf == UHDR_IMG_FMT_64bppRGBAHalfFloat) {
RET_IF_ERR(uhdr_enc_set_raw_image(handle, &mRawRgbaF16Image, UHDR_HDR_IMG))
}
}
if (mSdrIntentRawFile != nullptr) {
Expand Down Expand Up @@ -1336,7 +1370,8 @@ static void usage(const char* name) {
stderr,
" -y raw sdr intent input resource (8-bit), required for encoding scenarios 1, 2. \n");
fprintf(stderr,
" -a raw hdr intent color format, optional. [0:p010, 5:rgba1010102 (default)] \n");
" -a raw hdr intent color format, optional. [0:p010, 4: rgbahalffloat, "
"5:rgba1010102 (default)] \n");
fprintf(stderr,
" -b raw sdr intent color format, optional. [1:yuv420, 3:rgba8888 (default)] \n");
fprintf(stderr,
Expand All @@ -1353,6 +1388,12 @@ static void usage(const char* name) {
" -c sdr intent color gamut, optional. [0:bt709 (default), 1:p3, 2:bt2100] \n");
fprintf(stderr,
" -t hdr intent color transfer, optional. [0:linear, 1:hlg (default), 2:pq] \n");
fprintf(stderr,
" It should be noted that not all combinations of input color format and input "
"color transfer are supported. \n"
" srgb color transfer shall be paired with rgba8888 or yuv420 only. \n"
" hlg, pq shall be paired with rgba1010102 or p010. \n"
" linear shall be paired with rgbahalffloat. \n");
fprintf(stderr,
" -q quality factor to be used while encoding sdr intent, optional. [0-100], 95 : "
"default.\n");
Expand Down Expand Up @@ -1635,7 +1676,8 @@ int main(int argc, char* argv[]) {
appInput.convertRgba8888ToYUV444Image();
appInput.computeYUVSdrPSNR();
}
} else if (out_cf == UHDR_IMG_FMT_32bppRGBA1010102 && hdr_intent_raw_file != nullptr) {
} else if (out_cf == UHDR_IMG_FMT_32bppRGBA1010102 && hdr_intent_raw_file != nullptr &&
hdr_cf != UHDR_IMG_FMT_64bppRGBAHalfFloat) {
if (hdr_cf == UHDR_IMG_FMT_24bppYCbCrP010) {
appInput.convertP010ToRGBImage();
}
Expand Down
32 changes: 30 additions & 2 deletions java/UltraHdrApp.java
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ public class UltraHdrApp {
byte[] mYuv420YData, mYuv420CbData, mYuv420CrData;
short[] mP010YData, mP010CbCrData;
int[] mRgba1010102Data, mRgba8888Data;
long[] mRgbaF16Data;
byte[] mCompressedImageData;
byte[] mGainMapCompressedImageData;
byte[] mExifData;
Expand Down Expand Up @@ -197,6 +198,22 @@ public void fillRGBA1010102ImageHandle() throws IOException {
byteBuffer.asIntBuffer().get(mRgba1010102Data);
}

public void fillRGBAF16ImageHandle() throws IOException {
final int bpp = 8;
final int rgbSampleCount = mHeight * mWidth;
final int expectedSize = rgbSampleCount * bpp;
byte[] data = readFile(mHdrIntentRawFile);
if (data.length < expectedSize) {
throw new RuntimeException("For the configured width, height, RGBA1010102 Image File is"
+ " expected to contain " + expectedSize + " bytes, but the file has "
+ data.length + " bytes");
}
ByteBuffer byteBuffer = ByteBuffer.wrap(data);
byteBuffer.order(ByteOrder.nativeOrder());
mRgbaF16Data = new long[mHeight * mWidth];
byteBuffer.asLongBuffer().get(mRgbaF16Data);
}

public void fillRGBA8888Handle() throws IOException {
final int bpp = 4;
final int rgbSampleCount = mHeight * mWidth;
Expand Down Expand Up @@ -344,6 +361,10 @@ public void encode() throws Exception {
fillRGBA1010102ImageHandle();
handle.setRawImage(mRgba1010102Data, mWidth, mHeight, mWidth, mHdrCg, mHdrTf,
UHDR_CR_FULL_RANGE, mHdrCf, UHDR_HDR_IMG);
} else if (mHdrCf == UHDR_IMG_FMT_64bppRGBAHalfFloat) {
fillRGBAF16ImageHandle();
handle.setRawImage(mRgbaF16Data, mWidth, mHeight, mWidth, mHdrCg, mHdrTf,
UHDR_CR_FULL_RANGE, mHdrCf, UHDR_HDR_IMG);
} else {
throw new IllegalArgumentException("invalid hdr intent color format " + mHdrCf);
}
Expand Down Expand Up @@ -429,8 +450,8 @@ public static void usage() {
+ " scenarios 0, 1, 2, 3.");
System.out.println(" -y raw sdr intent input resource (8-bit), required for encoding"
+ " scenarios 1, 2.");
System.out.println(" -a raw hdr intent color format, optional. [0:p010, 5:rgba1010102"
+ " (default)]");
System.out.println(" -a raw hdr intent color format, optional. [0:p010, "
+ "4: rgbahalffloat, 5:rgba1010102 (default)]");
System.out.println(" -b raw sdr intent color format, optional. [1:yuv420, 3:rgba8888"
+ " (default)]");
System.out.println(" -i compressed sdr intent input resource (jpeg), required for "
Expand All @@ -447,6 +468,13 @@ public static void usage() {
" -c sdr intent color gamut, optional. [0:bt709 (default), 1:p3, 2:bt2100]");
System.out.println(
" -t hdr intent color transfer, optional. [0:linear, 1:hlg (default), 2:pq]");
System.out.println(
" It should be noted that not all combinations of input color format and"
+ " input color transfer are supported.");
System.out.println(
" srgb color transfer shall be paired with rgba8888 or yuv420 only.");
System.out.println(" hlg, pq shall be paired with rgba1010102 or p010.");
System.out.println(" linear shall be paired with rgbahalffloat.");
System.out.println(" -q quality factor to be used while encoding sdr intent, "
+ "optional. [0-100], 95 : default.");
System.out.println(" -R color range of hdr intent, optional. [0:narrow-range "
Expand Down
5 changes: 3 additions & 2 deletions java/com/google/media/codecs/ultrahdr/UltraHDRCommon.java
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,9 @@ public class UltraHDRCommon {
public static final int UHDR_IMG_FMT_32bppRGBA8888 = 3;

/**
* 64 bits per pixel RGBA color format, with 16-bit signed
* floating point red, green, blue, and alpha components.
* 64 bits per pixel, 16 bits per channel, half-precision floating point RGBA color format.
* In a pixel even though each channel has storage space of 16 bits, the nominal range is
* expected to be [0.0..(10000/203)]
* <p>
*
* <pre>
Expand Down
43 changes: 43 additions & 0 deletions java/com/google/media/codecs/ultrahdr/UltraHDREncoder.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import static com.google.media.codecs.ultrahdr.UltraHDRCommon.UHDR_IMG_FMT_24bppYCbCrP010;
import static com.google.media.codecs.ultrahdr.UltraHDRCommon.UHDR_IMG_FMT_32bppRGBA1010102;
import static com.google.media.codecs.ultrahdr.UltraHDRCommon.UHDR_IMG_FMT_32bppRGBA8888;
import static com.google.media.codecs.ultrahdr.UltraHDRCommon.UHDR_IMG_FMT_64bppRGBAHalfFloat;

import java.io.IOException;

Expand Down Expand Up @@ -117,6 +118,44 @@ public void setRawImage(int[] rgbBuff, int width, int height, int rgbStride, int
colorFormat, intent);
}

/**
* Add raw image info to encoder context. This interface is used for adding 64 bits-per-pixel
* packed formats. The function goes through all the arguments and checks for their sanity.
* If no anomalies are seen then the image info is added to internal list. Repeated calls to
* this function will replace the old entry with the current.
*
* @param rgbBuff rgb buffer handle
* @param width image width
* @param height image height
* @param rgbStride rgb buffer stride
* @param colorGamut color gamut of input image
* @param colorTransfer color transfer of input image
* @param colorRange color range of input image
* @param colorFormat color format of input image
* @param intent {@link UltraHDRCommon#UHDR_HDR_IMG} for hdr intent
* @throws IOException If parameters are not valid or current encoder instance is not valid
* or current encoder instance is not suitable for configuration
* exception is thrown
*/
public void setRawImage(long[] rgbBuff, int width, int height, int rgbStride, int colorGamut,
int colorTransfer, int colorRange, int colorFormat, int intent) throws IOException {
if (rgbBuff == null) {
throw new IOException("received null for image data handle");
}
if (width <= 0 || height <= 0) {
throw new IOException("received bad width and/or height, width or height is <= 0");
}
if (rgbStride <= 0) {
throw new IOException("received bad stride, stride is <= 0");
}
if (colorFormat != UHDR_IMG_FMT_64bppRGBAHalfFloat) {
throw new IOException("received unsupported color format. supported color formats are"
+ "{UHDR_IMG_FMT_64bppRGBAHalfFloat}");
}
setRawImageNative(rgbBuff, width, height, rgbStride, colorGamut, colorTransfer, colorRange,
colorFormat, intent);
}

/**
* Add raw image info to encoder context. This interface is used for adding 16 bits-per-sample
* pixel formats. The function goes through all the arguments and checks for their sanity. If
Expand Down Expand Up @@ -476,6 +515,10 @@ private native void setRawImageNative(int[] rgbBuff, int width, int height, int
int colorGamut, int colorTransfer, int colorRange, int colorFormat, int intent)
throws IOException;

private native void setRawImageNative(long[] rgbBuff, int width, int height, int rgbStride,
int colorGamut, int colorTransfer, int colorRange, int colorFormat, int intent)
throws IOException;

private native void setRawImageNative(short[] yBuff, short[] uvBuff, int width, int height,
int yStride, int uvStride, int colorGamut, int colorTransfer, int colorRange,
int colorFormat, int intent) throws IOException;
Expand Down
8 changes: 8 additions & 0 deletions java/jni/com_google_media_codecs_ultrahdr_UltraHDREncoder.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 613fee1

Please sign in to comment.