android/renderscript-intrinsics-replacement-toolkit

How to apply LUT3D from a file?

Opened this issue · 7 comments

Hello,

I am trying to apply LUT3D to a photo with an image that comes from a file(we have it as png or 3DL).

I am trying to read the png into a bitmap and read all pixels and add them to a Rgba3dArray but I don't know how.

I am not familiar with LUT3D and I can't get it working.

Input

image

I need to know how to put the pixels from the bitmap to a Rgba3dArray.
An example of how to do this would really help me.

Question

How do I set intensity to this filter?

I have the same question.

I'm in the same situation. This is all lower level than my comfort zone, an example that loads a LUT from drawable-nodpi/ would be very helpful. The code in the project generates a test LUT and is little more than a unit test, it's not a full implementation and I expect a lot of developers are a little lost trying to move away from RS Luts.

Ok, spent some time with this and here's a solution for 256x16 'Unreal' format LUTs:

import android.content.Context
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.graphics.Color
import com.google.android.renderscript.Rgba3dArray
import com.google.android.renderscript.Toolkit

/**
 * Converts Unreal format 256x16 LUT image to 3d Cube LUT
 */
class UnrealLutToolkit {

    private val unrealCube = LutUtils.Dimension(16, 16, 16)
    private var cubeArray: ByteArray? = null
    private var toolkitCube: Rgba3dArray? = null

    fun loadLut(context: Context, resourceId: Int){
        val lutBitmap = BitmapFactory.decodeResource(context.resources, resourceId)
        cubeArray = generateLutCube(lutBitmap, unrealCube)
        toolkitCube = Rgba3dArray(cubeArray!!, unrealCube.sizeX, unrealCube.sizeY, unrealCube.sizeZ)
    }

    fun process(source: Bitmap): Bitmap = Toolkit.lut3d(source, toolkitCube!!)

    private fun generateLutCube(lutBitmap: Bitmap, cubeSize: LutUtils.Dimension): ByteArray {
        val lutWidth = lutBitmap.width
        val lutHeight = lutBitmap.height
        val lutPixels = IntArray(lutWidth * lutHeight)
        lutBitmap.getPixels(lutPixels, 0, lutWidth, 0, 0, lutWidth, lutHeight)
        lutBitmap.recycle()//Done with Lut bitmap

        val data = ByteArray(cubeSize.sizeX * cubeSize.sizeY * cubeSize.sizeZ * 4)
        val cube = Rgba3dArray(data, cubeSize.sizeX, cubeSize.sizeY, cubeSize.sizeZ)

        for (red in 0 until 16) {
            for (green in 0 until 16) {
                val p = red + green * lutWidth
                for (blue in 0 until 16) {
                    val c = lutPixels[p + blue * 16]
                    cube[red, green, blue] = byteArrayOf(Color.red(c).toByte(), Color.green(c).toByte(), Color.blue(c).toByte(), (255).toByte())
                }
            }
        }

        return data
    }
}
trimf commented
val bitmap = ? // you've read the bitmap from some source using BitmapFactory, it is 512x512 8 bit

val size = 64
val lutWidth = 512
val rowCount = lutWidth / size
val columnCount = lutWidth / size
val cube = Rgba3dArray(
    ByteArray(size * size * size * 4),
    size,
    size,
    size
)

val pixels = IntArray(bitmap.width * bitmap.height)
bitmap.getPixels(pixels, 0, bitmap.width, 0, 0, bitmap.width, bitmap.height)

var p = 0
var z = 0
for (i in 0 until rowCount) {
    for (y in 0 until size) {
        val tmp = z
        for (j in 0 until columnCount) {
            for (x in 0 until size) {
                val pixel = pixels[p]
                cube[x, y, z] = byteArrayOf(
                    Color.red(pixel).toByte(),
                    Color.green(pixel).toByte(),
                    Color.blue(pixel).toByte(),
                    (255).toByte() // not clear how alpha works
                )
                p++
            }
            z++
        }
        z = tmp
    }
    z += columnCount
}

Thanks to ios, because they DO have variety of examples how to read bitmap to cube.

@trimf How do you set intensity? Maybe that's what alpha does

This is how I do it using above answers + setting intensity, hope it helps:

fun setFilter(source:Bitmap, lutBitmap: Bitmap, cubeSize: Int = 16, intensity: Float = 0.8f): Bitmap{
        val cubeArray = generateLutCube(lutBitmap, cubeSize)
        val toolkitCube = Rgba3dArray(cubeArray, cubeSize, cubeSize, cubeSize)
        val filteredBitmap = Toolkit.lut3d(source, toolkitCube)
        if(intensity == 1f) return filteredBitmap
        val bitmapWithIntensity = adjustOpacity(filteredBitmap, (255 * intensity).toInt())
        Toolkit.blend(BlendingMode.DST_OVER,source, bitmapWithIntensity)
        return bitmapWithIntensity
    }

private fun generateLutCube(bitmap: Bitmap, size: Int): ByteArray{
        val lutWidth = bitmap.width
        val lutHeight = bitmap.height
        val rowCount = lutWidth / size
        val columnCount = lutWidth / size
        val data = ByteArray(size * size * size * 4)
        val cube = Rgba3dArray(
            data,
            size,
            size,
            size
        )

        val pixels = IntArray(lutWidth * lutHeight)
        bitmap.getPixels(pixels, 0, lutWidth, 0, 0, lutWidth, lutHeight)

        var p = 0
        var z = 0
        for (i in 0 until rowCount) {
            for (y in 0 until size) {
                val tmp = z
                for (j in 0 until columnCount) {
                    for (x in 0 until size) {
                        val pixel = pixels[p]
                        cube[x, y, z] = byteArrayOf(
                            Color.red(pixel).toByte(),
                            Color.green(pixel).toByte(),
                            Color.blue(pixel).toByte(),
                            (255).toByte() // not clear how alpha works
                        )
                        p++
                    }
                    z++
                }
                z = tmp
            }
            z += columnCount
        }
        return data
    }

private fun adjustOpacity(bitmap: Bitmap, opacity: Int): Bitmap {
        val mutableBitmap =
            if (bitmap.isMutable) bitmap else bitmap.copy(Bitmap.Config.ARGB_8888, true)
        val canvas = Canvas(mutableBitmap)
        val colour = opacity and 0xFF shl 24
        canvas.drawColor(colour, PorterDuff.Mode.DST_IN)
        return mutableBitmap
    }

Set cubeSize to 16 if it's 4x4 and to 64 if it is 8x8

Thanks @Mads1337 . It's working.

Note: Lut and source bitmap size must be the same.