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
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
}
}
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.