通过查找表更改数据

时间:2017-12-26 08:11:26

标签: java android opencv lookup-tables opencv4android

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

对于初学者,我知道有一个Core.LUT()函数,但它在这里没有用,因为我不一定使用CvType.CV_8U类型Mat,查找表可能大于256个值。我将主要使用单通道浮动(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包含浮点数有点奇怪。当然我可以将它转换为某种整数类型矩阵/数组,但我仍然无法使用Core.LUT,因为我的LUT可能包含非整数值。

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

1 个答案:

答案 0 :(得分:0)

我最终找到了一个解决方案 - 不是将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)。具有大小为k的LUT的这个版本需要O(k),这对于通常情况k(&lt;&lt; Ñ