java - 通过查找表更改数据

标签 java android opencv lookup-tables opencv4android

在我正在开发的 Android 应用程序(使用 OpenCV4Android)中,我有一个包含整数值的矩阵(Mat 对象)和另一个作为查找表的行矩阵。我需要创建一个与第一个矩阵大小相同的新矩阵,其中每个值都替换为相应的查找表值(即 newMat(x,y) = lookupTable(oldMat(x,y)))。

对于初学者,我知道有一个 Core.LUT() 函数,但它在这里无济于事,因为我不一定要使用 CvType.CV_8U typed Mats,并且查找表可能大于 256 个值。我将主要使用单 channel float (CvType.CV_32FC1)。

我想到了这个解决方案:

// init matrices
Mat oldMat = initOldMat(); // contains values from 0 to k-1
Mat newMat = new Mat(oldMat.size(), oldMat.type());
Mat LUT = initLUT(); // a row matrix with k columns

// copying data from matrices to arrays
float[] oldMatArr = new float[(int)oldMat.total()];
oldMat.put(0,0,oldMarArr);
float[] lutArr= new float[(int)LUT.total()];
oldMat.put(0,0,lutArr);

// initialize array for the new matrix
float[] newMatArr = new float[oldMatArr.length];

// applying the LUT - see function below
applyLUTonData(oldMatArr, lutArr, newMatArr);

// copy the data to the new matrix
newMat.put(0, 0, newMatArr);



private void applyLUTonData(float[] inputData, float[] lut, float[] outputData) {

        if (inputData.length != outputData.length) {
            Log.e(TAG, "applyLUTonData: inputData.length != outputData.length", new IllegalArgumentException("inputData.length != outputData.length"));
        }
        if (Floats.min(inputData) < 0 || Floats.max(inputData) > lut.length) {
            Log.e(TAG, "applyLUTonData: Invalid values in inputData", new IllegalArgumentException("Invalid values in inputData"));
        }

        for (int i = 0; i < inputData.length; i++) {
            if (inputData[i] != Math.round(inputData[i])) { // not an int
                Log.e(TAG, "applyLUTonData: inputData contains non-integers", new IllegalArgumentException("inputData contains non-integers"));
            }
            outputData[i] = lut[(int) inputData[i]];
        }
    }

注意:我知道 inputData 包含 float 有点奇怪。当然我可以将它转换为一些整数类型的矩阵/数组,但我仍然无法使用 Core.LUT 因为我的 LUT 可能包含非整数值.

它可以正常工作,这很好但是它非常低效 - 这个函数在嵌套的 for 循环中被调用,最多 3*k 次( k 是LUT大小),需要花费很多时间。因此,我正在寻找一种实现相同功能的有效方法。它可以使用 Java 或 OpenCV 解决方案(包括像 Guave 等库)——无论哪个都有效。

最佳答案

我最终找到了一个解决方案 - 我没有将 Mat 对象转换为数组,而是将它们保存为 Mat,并将它们传递给函数。这个想法是将它“切片”到 LUT 中的值数量,一次处理每个值。例如 - 对于包含 32 个不同值的 LUT,我将检查输入数据(包含从 0 到 31 的整数),首先替换所有 0,然后替换 1,依此类推...

这是我的代码:

private static void applyLUTonData(Mat inputData, float[] LUT, Mat outputData, int nBins) {
        if (inputData.rows() != outputData.rows() || inputData.cols() != outputData.cols()) {
            Log.e(TAG, "applyLUTonData: inputData.size() != outputData.size()", new IllegalArgumentException("inputData.size() != outputData.size()"));
            return;
        }

        if (LUT.length != nBins) {
            Log.e(TAG, "applyLUTonData: Invalid length for LUT - " + nBins, new IllegalArgumentException("Invalid length for LUT - " + nBins));
            return;
        }

        Core.MinMaxLocResult res = Core.minMaxLoc(inputData);
        if (res.minVal < 0 || res.maxVal >= nBins) {
            Log.e(TAG, "applyLUTonData: Invalid inputData values", new IllegalArgumentException("Invalid inputData values"));
            return;
        }
        // here's the magic
        Mat cmpMat = new Mat();

        for (int i = 0; i < nBins; i++) {
            Core.compare(inputData, new Scalar(i), cmpMat, Core.CMP_EQ);
            outputData.setTo(new Scalar(LUT[i]), cmpMat);
        }
        cmpMat.release();
    }

就复杂性而言,对于 n 像素图像,我的旧版本花费了 O(n)。这个 LUT 大小为 k 的版本需要 O(k),这对于 k << n 的通常情况来说是一个很大的改进。

关于java - 通过查找表更改数据,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47975713/

相关文章:

c++ - 使用openCV捕获相机帧的延迟

android - Exoplayer 错误中的 Widevine Drm

java - 如何避免转换 CDATA 格式中存在的标签

java - 使用 Selenium Webdriver 并行测试多个浏览器

java - JScrollPane 不水平滚动

java - 使用来自在线的 Java 类列表填充 Spinner

java - 有没有办法使用 Android AWS SDK 从 Amazon S3 存储桶读取文件上下文,而无需下载文件?

java - android studio 中的 Opencv 不起作用?

python - OpenCV中围绕圆的锯齿状边缘-Python

java - 使用 DynamicJasper 打印报告中的自定义文本