Java – How do I use ScriptIntrinsic3DLUT with .cube files?

How do I use ScriptIntrinsic3DLUT with .cube files?… here is a solution to the problem.

How do I use ScriptIntrinsic3DLUT with .cube files?

First of all, I am not familiar with image processing in Android. I have a .cube file “generated by Resolve”, i.e. LUT_3D_SIZE 33. I’m trying to use android.support.v8.renderscript.ScriptIntrinsic3DLUT to apply a lookup table to process images. I’m assuming I should use ScriptIntrinsic3DLUT instead of android.support.v8.renderscript.ScriptIntrinsicLUT, right?

I’m having trouble looking for sample code to do this, so here’s what I’m putting together so far. The problem I’m having is how do I create a distribution from my .cube file?

...
final RenderScript renderScript = RenderScript.create(getApplicationContext());
final ScriptIntrinsic3DLUT scriptIntrinsic3DLUT = ScriptIntrinsic3DLUT.create(renderScript, Element.U8_4(renderScript));

 How to create an Allocation from .cube file?
final Allocation allocationLut = Allocation.createXXX();

scriptIntrinsic3DLUT.setLUT(allocationLut);

Bitmap bitmapIn = selectedImage;
Bitmap bitmapOut = selectedImage.copy(bitmapIn.getConfig(),true);

Allocation aIn = Allocation.createFromBitmap(renderScript, bitmapIn);
Allocation aOut = Allocation.createTyped(renderScript, aIn.getType());

aOut.copyTo(bitmapOut);
imageView.setImageBitmap(bitmapOut);
...

Any ideas?

Solution

Parse the .cube file

First of all, what you should do is parse the .cube file.
OpenColorIO shows how to do this in C++. It has some ways to parse LUT files like .cube, .lut, etc.
For example, FileFormatIridasCube.cpp Shows how
Process a .cube file.

You can easily pass
LUT_3D_SIZE。 I contacted an image processing algorithm engineer.
Here’s what he said:

  • In general, a cube of 17^3 is considered the highest quality output of preview, normal of 33^3, and highest quality output of 65^3.

Note that in the .cube file we can get 3*LUT_3D_SIZE^3 float.
The key point is how to handle float groups. We can’t use Allocation to set this array to a cube in ScriptIntrinsic3DLUT.
Before we can do this, we need to deal with float groups.

Process data from .cube files

For all we know, if it is 8-bit depth, each RGB component is an 8-bit int.
R is in the upper 8 bits, G is in the middle, and B is in the lower 8 bits. Such a 24-bit int could contain these
Contains three components at the same time.

In a .cube file, each data row contains 3 floats.
Please note: The blue component is in the first !!!

I came to this conclusion from trial and error. (Or who can give a more accurate explanation.) )

Each float represents the coefficient of the component by 255, so we need to calculate the real one
The value of these three components:

int getRGBColorValue(float b, float g, float r) {
    int bcol = (int) (255 * clamp(b, 0.f, 1.f));
    int gcol = (int) (255 * clamp(g, 0.f, 1.f));
    int rcol = (int) (255 * clamp(r, 0.f, 1.f));
    return bcol | (gcol << 8) | (rcol << 16);
}

So we can get an integer from each row of data, which contains 3 floats.
Finally, we get an array of integers with a length of LUT_3D_SIZE^3. This array is expected to be
Apply to a cube.

ScriptIntrinsic3DLUT

RsLutDemo shows how to apply ScriptIntrinsic3DLUT.

RenderScript mRs;
Bitmap mBitmap;
Bitmap mLutBitmap;
ScriptIntrinsic3DLUT mScriptlut;
Bitmap mOutputBitmap;
Allocation mAllocIn;
Allocation mAllocOut;
Allocation mAllocCube;
...
int redDim, greenDim, blueDim;
int[] lut;
if (mScriptlut == null) {
    mScriptlut = ScriptIntrinsic3DLUT.create(mRs, Element.U8_4(mRs));
}
if (mBitmap == null) {
    mBitmap = BitmapFactory.decodeResource(getResources(),
            R.drawable.bugs);

mOutputBitmap = Bitmap.createBitmap(mBitmap.getWidth(), mBitmap.getHeight(), mBitmap.getConfig());

mAllocIn = Allocation.createFromBitmap(mRs, mBitmap);
    mAllocOut = Allocation.createFromBitmap(mRs, mOutputBitmap);
}
...
 get the expected lut[] from .cube file.
...
Type.Builder tb = new Type.Builder(mRs, Element.U8_4(mRs));
tb.setX(redDim).setY(greenDim).setZ(blueDim);
Type t = tb.create();
mAllocCube = Allocation.createTyped(mRs, t);
mAllocCube.copyFromUnchecked(lut);

mScriptlut.setLUT(mAllocCube);
mScriptlut.forEach(mAllocIn, mAllocOut);

mAllocOut.copyTo(mOutputBitmap);

Demo

I have completed a presentation showing my work.
You can view it on Github
Thank you.

Related Problems and Solutions