You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android开发:如何加载.cube文件并对Bitmap执行LUT操作?

Android加载.cube文件实现Bitmap LUT滤镜方案

一、先搞定.cube文件的正确解析

你的.cube文件开头的LUT_3D_SIZE 16表示这是16x16x16的3D查找表,总共有16*16*16=4096个RGB颜色项,每个项是0-1范围的浮点值。解析时要跳过注释(#开头)、空行,先提取尺寸,再读取所有颜色值。

解析代码(Kotlin)

fun loadCubeFromAssets(assets: AssetManager, fileName: String): FloatArray? {
    return try {
        assets.open(fileName).bufferedReader().use { reader ->
            var lutSize = 0
            val colorValues = mutableListOf<Float>()
            reader.lineSequence().forEach { line ->
                val trimmedLine = line.trim()
                // 跳过空行和注释
                if (trimmedLine.isEmpty() || trimmedLine.startsWith("#")) return@forEach
                // 提取LUT尺寸
                if (trimmedLine.startsWith("LUT_3D_SIZE")) {
                    lutSize = trimmedLine.split(" ")[1].toInt()
                } else {
                    // 读取RGB三个浮点值
                    trimmedLine.split(" ").filter { it.isNotEmpty() }.forEach { value ->
                        colorValues.add(value.toFloat())
                    }
                }
            }
            // 验证数据长度:3*lutSize^3
            if (lutSize > 0 && colorValues.size == 3 * lutSize * lutSize * lutSize) {
                colorValues.toFloatArray()
            } else {
                null // 解析失败,数据不完整
            }
        }
    } catch (e: Exception) {
        e.printStackTrace()
        null
    }
}

二、高效实现:用RenderScript做LUT处理

RenderScript是Android官方的高性能图像处理框架,适合大尺寸Bitmap,效率比直接遍历像素高很多。

1. 创建RenderScript内核文件(lut_script.rs,放在src/main/rs/目录下)

#pragma version(1)
#pragma rs java_package_name(com.your.package.name)

// 3D LUT数组,长度为3*size^3
rs_allocation lut;
int lutSize;

// 将0-255的颜色值转成0-1范围的比例
static float norm(float x) {
    return x / 255.0f;
}

uchar4 __attribute__((kernel)) applyLUT(uchar4 in, uint32_t x, uint32_t y) {
    // 提取输入像素的RGB(注意Bitmap是ARGB,所以in.r是红色,in.g绿色,in.b蓝色)
    float r = norm(in.r);
    float g = norm(in.g);
    float b = norm(in.b);

    // 计算LUT中的索引(3D转一维)
    float sizeMinus1 = lutSize - 1.0f;
    int rIndex = clamp((int)(r * sizeMinus1), 0, lutSize - 1);
    int gIndex = clamp((int)(g * sizeMinus1), 0, lutSize - 1);
    int bIndex = clamp((int)(b * sizeMinus1), 0, lutSize - 1);

    int baseIndex = (rIndex * lutSize + gIndex) * lutSize + bIndex;
    // 从LUT中读取转换后的RGB值
    float outR = rsGetElementAt_float(lut, baseIndex * 3 + 0);
    float outG = rsGetElementAt_float(lut, baseIndex * 3 + 1);
    float outB = rsGetElementAt_float(lut, baseIndex * 3 + 2);

    // 转成0-255的uchar值,返回ARGB格式
    return uchar4(
        (outR * 255.0f + 0.5f) as uchar,
        (outG * 255.0f + 0.5f) as uchar,
        (outB * 255.0f + 0.5f) as uchar,
        in.a // 保留原透明度
    );
}

2. Java/Kotlin调用RenderScript代码

fun applyLUTWithRenderScript(context: Context, sourceBitmap: Bitmap, lutArray: FloatArray, lutSize: Int): Bitmap? {
    // 确保Bitmap是ARGB_8888格式
    val inputBitmap = if (sourceBitmap.config!= Bitmap.Config.ARGB_8888) {
        sourceBitmap.copy(Bitmap.Config.ARGB_8888, true)
    } else {
        sourceBitmap
    }
    val outputBitmap = Bitmap.createBitmap(inputBitmap.width, inputBitmap.height, Bitmap.Config.ARGB_8888)

    val rs = RenderScript.create(context)
    val inputAllocation = Allocation.createFromBitmap(rs, inputBitmap)
    val outputAllocation = Allocation.createFromBitmap(rs, outputBitmap)

    // 加载LUT脚本
    val script = ScriptC_lut_script(rs)
    // 创建LUT的Allocation
    val lutAllocation = Allocation.createSized(rs, Element.F32(rs), lutArray.size)
    lutAllocation.copyFrom(lutArray)

    // 设置脚本参数
    script._lut = lutAllocation
    script._lutSize = lutSize

    // 执行内核
    script.forEach_applyLUT(inputAllocation, outputAllocation)

    // 将结果写入Bitmap
    outputAllocation.copyTo(outputBitmap)

    // 释放资源
    inputAllocation.destroy()
    outputAllocation.destroy()
    lutAllocation.destroy()
    script.destroy()
    rs.destroy()

    return outputBitmap
}

三、兼容方案:直接遍历Bitmap像素替换

如果你的App需要兼容低版本(API<17)或者不想用RenderScript,可以用这种方法,缺点是大尺寸Bitmap会比较慢。

fun applyLUTDirectly(sourceBitmap: Bitmap, lutArray: FloatArray, lutSize: Int): Bitmap? {
    val inputBitmap = if (sourceBitmap.config!= Bitmap.Config.ARGB_8888) {
        sourceBitmap.copy(Bitmap.Config.ARGB_8888, true)
    } else {
        sourceBitmap
    }
    val outputBitmap = Bitmap.createBitmap(inputBitmap.width, inputBitmap.height, Bitmap.Config.ARGB_8888)

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

    val sizeMinus1 = lutSize - 1.0f
    for (i in pixels.indices) {
        val pixel = pixels[i]
        // 提取ARGB
        val a = pixel shr 24 and 0xFF
        val r = pixel shr 16 and 0xFF
        val g = pixel shr 8 and 0xFF
        val b = pixel and 0xFF

        // 转成0-1范围,计算LUT索引
        val rNorm = r / 255.0f
        val gNorm = g / 255.0f
        val bNorm = b / 255.0f

        val rIndex = (rNorm * sizeMinus1).toInt().coerceIn(0, lutSize - 1)
        val gIndex = (gNorm * sizeMinus1).toInt().coerceIn(0, lutSize - 1)
        val bIndex = (bNorm * sizeMinus1).toInt().coerceIn(0, lutSize - 1)

        val baseIndex = (rIndex * lutSize + gIndex) * lutSize + bIndex * 3
        // 读取LUT中的RGB值,转成0-255
        val outR = (lutArray[baseIndex] * 255 + 0.5f).toInt().coerceIn(0, 255)
        val outG = (lutArray[baseIndex + 1] * 255 + 0.5f).toInt().coerceIn(0, 255)
        val outB = (lutArray[baseIndex + 2] * 255 + 0.5f).toInt().coerceIn(0, 255)

        // 重新组合像素
        pixels[i] = a shl 24 or (outR shl 16) or (outG shl 8) or outB
    }

    outputBitmap.setPixels(pixels, 0, outputBitmap.width, 0, 0, outputBitmap.width, outputBitmap.height)
    return outputBitmap
}

四、常见问题排查

  1. 解析错误:检查.cube文件是否完整,有没有额外的空行或注释,确保颜色值总数是3*lutSize^3。比如你的16尺寸,应该有3*4096=12288个浮点值。
  2. 颜色偏差:注意.cube里的数值是0-1范围,转成Bitmap的0-255时要乘255并加0.5f做四舍五入,避免精度丢失。
  3. 索引计算错误:3D LUT的索引顺序通常是R→G→B,也就是(R索引 * 尺寸 + G索引) * 尺寸 + B索引,别搞反顺序。
  4. Bitmap格式:必须用ARGB_8888格式,其他格式(如RGB_565)会导致颜色通道读取错误。

内容的提问来源于stack exchange,提问作者白小花

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.10 16:34:57