如何在Android中为位图图像应用颜色LUT以实现滤镜效果?

5

我在安卓中有关于LUT的问题。

我的问题是,我有4X4的LUTs,在安卓中使用这些LUTs为位图图像应用滤镜效果。以下是我的示例LUT文件链接。 Lut link sample

在安卓中是否可行?如果可行,请帮我解决如何应用的问题。

提前致谢。

3个回答

4
我正在开发一款LUT应用程序库,以便在Android中更轻松地使用LUT图像。它使用下面的算法,但我希望将来能够增强它,以优化内存使用。现在它还会猜测LUT的颜色轴: https://github.com/dntks/easyLUT/wiki 您的LUT图像的红绿蓝色维度与我所习惯的不同,因此我必须更改获取lutIndex(在getLutIndex()处)的顺序。 请检查我的编辑答案:
final static int X_DEPTH = 16;
final static int Y_DEPTH = 16; //One little square has 16x16 pixels in it
final static int ROW_DEPTH = 4;
final static int COLUMN_DEPTH = 4; // the image consists of 4x4 little squares
final static int COLOR_DISTORTION = 16; // 256*256*256 => 256 no distortion, 64*64*64 => 256 dividied by 4 = 64, 16x16x16 => 256 dividied by 16 = 16

private Bitmap applyLutToBitmap(Bitmap src, Bitmap lutBitmap) {
    int lutWidth = lutBitmap.getWidth();
    int lutColors[] = new int[lutWidth * lutBitmap.getHeight()];
    lutBitmap.getPixels(lutColors, 0, lutWidth, 0, 0, lutWidth, lutBitmap.getHeight());

    int mWidth = src.getWidth();
    int mHeight = src.getHeight();
    int[] pix = new int[mWidth * mHeight];
    src.getPixels(pix, 0, mWidth, 0, 0, mWidth, mHeight);

    int R, G, B;
    for (int y = 0; y < mHeight; y++)
        for (int x = 0; x < mWidth; x++) {
            int index = y * mWidth + x;
            int r = ((pix[index] >> 16) & 0xff) / COLOR_DISTORTION;
            int g = ((pix[index] >> 8) & 0xff) / COLOR_DISTORTION;
            int b = (pix[index] & 0xff) / COLOR_DISTORTION;

            int lutIndex = getLutIndex(lutWidth, r, g, b);

            R = ((lutColors[lutIndex] >> 16) & 0xff);
            G = ((lutColors[lutIndex] >> 8) & 0xff);
            B = ((lutColors[lutIndex]) & 0xff);
            pix[index] = 0xff000000 | (R << 16) | (G << 8) | B;
        }
    Bitmap filteredBitmap = Bitmap.createBitmap(mWidth, mHeight, src.getConfig());
    filteredBitmap.setPixels(pix, 0, mWidth, 0, 0, mWidth, mHeight);
    return filteredBitmap;
}

//the magic happens here
private int getLutIndex(int lutWidth, int redDepth, int greenDepth, int blueDepth) {
    int lutX = (greenDepth % ROW_DEPTH) * X_DEPTH + blueDepth;
    int lutY = (greenDepth / COLUMN_DEPTH) * Y_DEPTH + redDepth;
    return lutY * lutWidth + lutX;
}

谢谢您的回复。我认为这不是问题所在。如果您不介意,能否上传您的LUT以检查您的LUT与我的差异... - user512
我已经更新了代码以适应您的查找表,请检查一下! - abbath
它解决了你的问题吗?如果是,请接受这个答案。如果 LUT 图像在不同设备上被调整大小,将其放置在 drawable-nodpi 文件夹中。 - abbath
不,我在两个设备上测试过(三星S4和联想A7000)...它在联想上可以工作,但在S4上无法工作...这意味着滤镜会变得有点阴暗。 - user512
你把LUT图像放在哪个资源文件夹里了? - abbath
显示剩余14条评论

2

您可以参考以下步骤,希望能帮您找到正确的流程。

这里的主要位图是照片。

mLut3D是存储在drawable中的LUT图像数组。

    RenderScript mRs;
    Bitmap mLutBitmap, mBitmap;
    ScriptIntrinsic3DLUT mScriptlut;
    Bitmap mOutputBitmap;
    Allocation mAllocIn;
    Allocation mAllocOut;
    Allocation mAllocCube;
    int mFilter = 0;

mRs = RenderScript.create(yourActivity.this);
public Bitmap filterapply() {
        int redDim, greenDim, blueDim;
        int w, h;
        int[] lut;

        if (mScriptlut == null) {
            mScriptlut = ScriptIntrinsic3DLUT.create(mRs, Element.U8_4(mRs));
        }
         if (mBitmap == null) {
         mBitmap = photo;
}
        mOutputBitmap = Bitmap.createBitmap(mBitmap.getWidth(),
                mBitmap.getHeight(), mBitmap.getConfig());

        mAllocIn = Allocation.createFromBitmap(mRs, mBitmap);
        mAllocOut = Allocation.createFromBitmap(mRs, mOutputBitmap);
        // }

        mLutBitmap = BitmapFactory.decodeResource(getResources(),
                mLut3D[mFilter]);
        w = mLutBitmap.getWidth();
        h = mLutBitmap.getHeight();
        redDim = w / h;
        greenDim = redDim;
        blueDim = redDim;
        int[] pixels = new int[w * h];
        lut = new int[w * h];
        mLutBitmap.getPixels(pixels, 0, w, 0, 0, w, h);
        int i = 0;

        for (int r = 0; r < redDim; r++) {
            for (int g = 0; g < greenDim; g++) {
                int p = r + g * w;
                for (int b = 0; b < blueDim; b++) {
                    lut[i++] = pixels[p + b * h];
                }
            }
        }

        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);
        return mOutputBitmap;
    }

通过增加mFilter的值,可以在不同的LUT图像中获得不同的滤镜效果,请查看。

您可以通过访问Github上的该链接了解更多帮助信息,我是从这里获取答案的: https://github.com/RenderScript/RsLutDemo

希望这能够有所帮助。


很棒的答案。感谢您的帮助。这个 Github 链接真的很有效。 - A.TNG
嗨,Shubham,关于RsLutDemo,你知道drawable-nodpi/lut_bleach.png是怎么来的吗?有什么工具可以生成这些文件吗?非常感谢。 - A.TNG
你好 A.TNG,实际上 LUT 图像是由图形设计师创建的,你可以向他们索要滤镜 LUT 图像,他们只需要学习一下就可以了。你也可以查看这个链接或类似的其他链接。 - Shubham

2
这是使用RenderScript的ScriptIntrinsic3DLUT处理图像的方法。
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;
import android.os.Bundle;
import android.renderscript.Allocation;
import android.renderscript.Element;
import android.renderscript.RenderScript;
import android.renderscript.ScriptIntrinsic3DLUT;
import android.renderscript.Type;
import android.support.v7.app.AppCompatActivity;
import android.widget.ImageView;

public class MainActivity extends AppCompatActivity {
    ImageView imageView1;
    RenderScript mRs;
    Bitmap mBitmap;
    Bitmap mLutBitmap;
    ScriptIntrinsic3DLUT mScriptlut;
    Bitmap mOutputBitmap;
    Allocation mAllocIn;
    Allocation mAllocOut;
    Allocation mAllocCube;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        imageView1 = (ImageView) findViewById(R.id.imageView);
        mRs = RenderScript.create(this);
        Background background = new Background();
        background.execute();
    }

    class Background extends AsyncTask<Void, Void, Void> {

        @Override
        protected Void doInBackground(Void... params) {
            if (mRs == null) {
                mRs = RenderScript.create(MainActivity.this);
            }
            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);
            }
            if (mLutBitmap == null) {
                mLutBitmap = BitmapFactory.decodeResource(getResources(),
                        R.drawable.dawizfe);
                int w = mLutBitmap.getWidth();
                int h = mLutBitmap.getHeight();
                int redDim = w / 4;
                int greenDim = h / 4;
                int blueDim = 16;
                android.renderscript.Type.Builder tb = new Type.Builder(mRs, Element.U8_4(mRs));
                tb.setX(redDim);
                tb.setY(greenDim);
                tb.setZ(blueDim);
                Type t = tb.create();
                mAllocCube = Allocation.createTyped(mRs, t);
                int[] pixels = new int[w * h];
                int[] lut = new int[w * h];
                mLutBitmap.getPixels(pixels, 0, w, 0, 0, w, h);
                int i = 0;
                for (int r = 0; r < redDim; r++) {
                   for (int g = 0; g < greenDim; g++) {
                        for (int b = 0; b < blueDim; b++) {
                            int gdown = g / 4;
                            int gright = g % 4;
                            lut[i] = pixels[b + r * w + gdown * w * redDim + gright * blueDim];
                            i++;
                        }
                    }
                }
//                This is an identity 3D LUT
//                i = 0;
//                for (int r = 0; r < redDim; r++) {
//                    for (int g = 0; g < greenDim; g++) {
//                        for (int b = 0; b < blueDim; b++) {
//                            int bcol = (b * 255) / blueDim;
//                            int gcol = (g * 255) / greenDim;
//                            int rcol = (r * 255) / redDim;
//                            lut[i] = bcol | (gcol << 8) | (rcol << 16);
//                            i++;
//                        }
//                    }
//                }
                mAllocCube.copyFromUnchecked(lut);
            }
            if (mScriptlut == null) {
                mScriptlut = ScriptIntrinsic3DLUT.create(mRs, Element.U8_4(mRs));

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

            mAllocOut.copyTo(mOutputBitmap);
            return null;
        }

        @Override
        protected void onPostExecute(Void aVoid) {
            imageView1.setImageBitmap(mOutputBitmap);
        }
    }
}

抱歉回复晚了。我尝试了这个,但我认为它不正确... 我只是用http://imgur.com/W7AOJkv和http://imgur.com/Ai2k1PV替换了drawables,在我使用这些drawables实现你的代码后,结果是http://imgur.com/OLMXKHf。但实际输出应该像这样http://imgur.com/OqGHMVU... 你有什么想法吗?请指导我。 - user512
你的图像解压逻辑有误,我已经修复了。 - user2985410
它只适用于某些LUT。还需要对其余的滤镜进行哪些更改? - user512
它在所有设备上都无法工作。例如:我在两个设备上检查了,如三星S4和联想A7000。在联想上,lutBitmap的宽度和高度为64 * 64,在S4上则为96 * 96。所以在S4上,在这一行代码lut[i] = pixels[b + r * w + gdown * w * redDim + gright * blueDim];中,我得到了数组越界异常。请检查一下。 - user512
对于LUT图像大小的问题:请将LUT图像放置在“drawable-nodpi”文件夹中! - abbath
我在互联网上找到的最简单的表达式是:lut[i] = pixels[b + r * w + gdown * w * redDim + gright * blueDim]; 谢谢。 - Bharat Kumar Molleti

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接