2012. 7. 10. 17:37

[android] 영상처리 NDK 단에서 처리하여 리턴하기 (Image Processing in NDK)


서론 : 이 과정은 아래글인 JNI 설정법을 먼저 읽어두고 숙지한 상태로 진행 한다는 것을 전제로 하겠다. 

안드로이드에서 카메라색상 값을 추출하면 그 값은 YUV 값으로 추출된다. 하지만 영상처리를 위해서는 YUV값보단 RGB 값으로 도될려주는 것이 더 간편하다.


때문에 안드로이드에서 YUV 값을 RGB 값으로 변환 하여야하는데, 인터넷에서 돌아다니는 소스는 대부분 자바단에서 YUV->RGB 로의 변환이 많다. 이렇게 될 경우 실시간 이미지 프로세싱을 하게되면 속도가 NDK 보다 느려짐이 발생 할 수 있다.


때문에 YUV 값을 C언어단으로 보내어 RGB 처리를 한후 이미지 프로세싱을 하고 그결과 값을 리턴하는 식의 진행을 하게 되었다. 



순서는 다음과 같다 


1. native 메소드를 생성 후 C언어 헤더 만들기.

2. 그 헤더의 전처리 함수를 이용하여 .c 파일의 RGV 값 추출 코드 만들기.

3. 포인터 형태로 RGB 값을 리턴하기. 

4. 리턴한 값을 비트맵으로 만들기.

5. ImageView 로 띄우기.


1.native 메소드를 생성 후 C언어 헤더 만들기.



맨아레 코드처럼 private native void 형으로 함수명을 선어해 주면 javah 명령어를 통해 C언어와 연결되는 헤더가 만들어 지게 된다. 


이 헤더 값을 기반으로 C언어단에서 Byte 값을 입력받아, Bitmap 파일로 리턴해 주는 함수를 짜야한다 사용한 코드는 너무 길어서 맨 아래 source_1 로 첨부 하도록 하겠다. (흐름이 깨지지 않도록)



그리고 android.mk 파일을 수정 해야 한다. (자세한것은 JNI 사용법 문서를 참조)

android.mk 파일 내용은 다음과 같다.(기존 JNI 문서보다 LOCAL_LDLIBS 환경변수가 추가되는데, 필자도 어떤 설정인지 파악을 하진 못했다.






위와같이 LOCAL_LDLIBS 환경변수를 추가적으로 주고 LOCAL_MODULE 에는 만들어질 라이브러리명, LOCAL_SRC_FILES 는 C파일명 을 설정해서 .c .h android.mk 파일 세개를 JNI 폴더로 옴긴 뒤 빌드 한다.





이후 자바단에서 그 함수(메소드)를 이용하여 비트맵을 전송시킨다. (콜백 함수를 이용한다) 


  

위그림에서  초록색 박스가 필자의 블로그에서 소개했던 기본 [Camera 를 surface 뷰로 띄우기] 에서 변경된 부분이다.

callback 함수에서 mainActivity의 ImageView 값을 변경해 주기 위해서 MainActivity를 인자값으로 넘겨서 실행 시키게 했다.



아래 빨간색 네모박스는 mHolder에 callback 함수를 추가시키는 부분이다. 별다른 내용은 없다.

그리고 노란색 박스는 콜백함수의 구현부이다.


중요한 부분은 파란색 박스의 prBitmap 부분이다. 이부분에서 빈 bitmap 파일을 ARGB8888 형태로 선언해두고, 리턴한 바이트 값을 비트맵으로 받아올 준비를 한다. 


그리고 그 윗부분 노란색 박스 아래에 OnPreviewFrame 부분인데, 이 메소드가 선언되면 인자값으로 카메라 값과 그 카메라의 바이트가 전송되는데 이값이 YUV 값이 되는것이다. (이 부분때문에 이해하느라 고생함)


이값을 바탕으로 native 함수인 YUB420SPtoRGB8888NEW 함수에 data를 넣고 prBitmap 으로 리턴 받게 된다.

이값을 이미지뷰에 띄워주면 된다.




결론.

이렇게 카메라 값을 native 에 넣었고 RGB로 변환 후 꺼냈으므로, 그사이에 영상처리 기술을 얼마든지 넣을 수 있다.

필요한 부분은 차차 구현해 나가고 결국엔 비트맵으로 출력해오면된다. 


중요한건 속도인데, 16~40프레임 선에서 처리가 된다. 






source_1 

 /**************************************************************************

* implements by Jeon (poemer@kut.ac.kr) 2012.05.13

* interface method Android - JNI - Native C 

* YUV420SP Converts to RGB 8888 Format

* this routines are optimized on ARM based CPU

***************************************************************************/

/*android specific headers*/

#include <jni.h>

#include <android/log.h>

#include <android/bitmap.h>


/*standard library*/

#include <time.h>

#include <math.h>

#include <limits.h>

#include <stdio.h>

#include <stdlib.h>

#include <inttypes.h>

#include <unistd.h>

#include <assert.h>

#include <string.h>


#include "edge_smash_CameraView.h"

#define LOG_TAG "YUV2RGB_Native"

#define LOG_LEVEL 10

#define LOGI(level, ...) if (level <= LOG_LEVEL) {__android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__);}

#define LOGE(level, ...)if (level <= LOG_LEVEL) {__android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__);}

inline int32_t toInt(jbyte pValue) {

return (0xff & (int32_t) pValue);

}

inline int32_t max(int32_t pValue1, int32_t pValue2) {

if (pValue1 < pValue2) {

return pValue2;

} else {

return pValue1;

}

}

inline int32_t clamp(int32_t pValue, int32_t pLowest, int32_t pHighest) {

if (pValue < 0) {

return pLowest;

} else if (pValue > pHighest) {

return pHighest;

} else {

return pValue;

}

}

inline int32_t color(pColorR, pColorG, pColorB) {

return 0xFF000000 | ((pColorB << 6) & 0x00FF0000) | ((pColorG >> 2) & 0x0000FF00) | ((pColorR >> 10) & 0x000000FF);

}


JNIEXPORT void JNICALL Java_edge_smash_CameraView_NdkTest(JNIEnv * penv, jobject object, jint a){

LOGE(1, "AndroidBitmap_getInfo failed! error = %d",a);

// return a+11;

}

JNIEXPORT void JNICALL Java_edge_smash_CameraView_YUV420SPtoRGB8888NEW(JNIEnv * pEnv, jobject pObj, jobject pBitmap, jbyteArray pinArray) {

AndroidBitmapInfo lBitmapInfo;

uint32_t* lBitmapContent;

int lRet;

// LOGE(1, "**IN JNI bitmap converter IN!");

//1. retrieve information about the bitmap

    if ((lRet = AndroidBitmap_getInfo(pEnv, pBitmap, &lBitmapInfo)) < 0) {

        LOGE(1, "AndroidBitmap_getInfo failed! error = %d", lRet);

        return;

    }

if (lBitmapInfo.format != ANDROID_BITMAP_FORMAT_RGBA_8888) {

LOGE(1, "Bitmap format is not RGBA_8888!");

return;

}

//2. lock the pixel buffer and retrieve a pointer to it

    if ((lRet = AndroidBitmap_lockPixels(pEnv, pBitmap, (void**)&lBitmapContent)) < 0) {

        LOGE(1, "AndroidBitmap_lockPixels() failed! error = %d", lRet);

return;

    }

jbyte* lSource = (*pEnv)->GetPrimitiveArrayCritical(pEnv, pinArray, 0);

if (lSource == NULL) {

LOGE(1, "Source is null");

return;

}

//LOGE(1, "**Start JNI bitmap converter ");


int32_t lFrameSize = lBitmapInfo.width * lBitmapInfo.height;

int32_t lYIndex, lUVIndex;

int32_t lX, lY;

int32_t lColorY, lColorU, lColorV;

int32_t lColorR, lColorG, lColorB;

int32_t y1192;

// Processes each pixel and converts YUV to RGB color.

for (lY = 0, lYIndex = 0; lY < lBitmapInfo.height; ++lY) {

lColorU = 0; lColorV = 0;

// Y is divided by 2 because UVs are subsampled vertically.

// This means that two consecutives iterations refer to the

// same UV line (e.g when Y=0 and Y=1).

lUVIndex = lFrameSize + (lY >> 1) * lBitmapInfo.width;


for (lX = 0; lX < lBitmapInfo.width; ++lX, ++lYIndex) {

// Retrieves YUV components. UVs are subsampled

// horizontally too, hence %2 (1 UV for 2 Y).

lColorY = max(toInt(lSource[lYIndex]) - 16, 0);

if (!(lX % 2)) {

lColorV = toInt(lSource[lUVIndex++]) - 128;

lColorU = toInt(lSource[lUVIndex++]) - 128;

}

// Computes R, G and B from Y, U and V.

y1192 = 1192 * lColorY;

lColorR = (y1192 + 1634 * lColorV);

lColorG = (y1192 - 833 * lColorV - 400 * lColorU);

lColorB = (y1192 + 2066 * lColorU);

lColorR = clamp(lColorR, 0, 262143);

lColorG = clamp(lColorG, 0, 262143);

lColorB = clamp(lColorB, 0, 262143);

// Combines R, G, B and A into the final pixel color.

lBitmapContent[lYIndex] = color(lColorR,lColorG,lColorB);

}

}

LOGE(1, "**Start JNI bitmap converter %d",lColorR);


(*pEnv)-> ReleasePrimitiveArrayCritical(pEnv,pinArray,lSource,0);

AndroidBitmap_unlockPixels(pEnv, pBitmap);

LOGI(1, "end color conversion2");

}









Posted by k1rha