2017-12-26 22 views
1

Android 앱에서 OpenCV4Android를 사용하여 개발 중입니다. 정수 값이 포함 된 매트릭스 (Mat 개체)와 조회 테이블 인 다른 행 매트릭스가 있습니다. 각 행의 값이 해당 조회 테이블 값 (예 : newMat(x,y) = lookupTable(oldMat(x,y)))으로 바뀌는 첫 번째 행의 크기로 새 행렬을 만들어야합니다. 우선 들어lookuptable로 데이터 변경

, 나는 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가 수레를 포함 조금 이상한 것을 알고있다. 물론 일부 정수형/행렬을 변환 할 수 있지만 LUT에 정수가 아닌 값이 포함되어 있기 때문에 여전히 Core.LUT을 사용할 수 없습니다. 이 기능은 3*k 번 (k는 LUT의 크기)까지, 루프 중첩 호출, 그리고 시간이 많이 걸립니다 -

하지만 매우 비효율적 인 노력하고 있습니다. 그래서, 나는 동일한 기능을 달성하는 효율적인 방법을 찾고있다. Java 또는 OpenCV 솔루션 (Guave 등의 라이브러리 포함) 중 어느 것이 든 작동합니다.

+0

32C 버전의 8UC1 코드를 적용 할 수 있는지 여부를 opencv 소스 코드에서 살펴볼 수 있습니다. – Micka

+0

@Micka 나는 그것을 보려고했지만, C++로 작성되었고, Java로 변환하는 것이 쉽지는 않다. – noamgot

답변

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 < < n과 같은 일반적인 경우에 큰 개선점입니다.