在我正在开发的 Android 应用程序(使用 OpenCV4Android)中,我有一个包含整数值的矩阵(Mat
对象)和另一个作为查找表的行矩阵。我需要创建一个与第一个矩阵大小相同的新矩阵,其中每个值都替换为相应的查找表值(即 newMat(x,y) = lookupTable(oldMat(x,y))
)。
对于初学者,我知道有一个 Core.LUT()
函数,但它在这里无济于事,因为我不一定要使用 CvType.CV_8U
typed Mat
s,并且查找表可能大于 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/