최근에 안드로이드와 iOS에서 openCV를 사용할 일이 생겼다.
환경설정까지는 다른 분께서 해주셔서 큰 신경 쓰지 않고,
바뀐 언어 환경에서 같은 로직을 적용해본 결과 생각보다 느렸다.
오랜만에 급하게 하다가 깜빡한 게 생각나서 기록하는 겸,
왜 느려졌는지 그 이유와 개선방법에 대해서 적어보려고 한다.
먼저 로직을 간단하게 적어보자면 이렇다.
- 이미지를 받아온다.
- ROI 관심영역을 추출한다.
- 추출한 영역 내에서 픽셀마다 값을 변경한다.
- 변경된 이미지를 내보낸다.
openCV를 써본 사람이면 아주 쉽게 접하는 일이다.
안드로이드에서 간단하게 예시를 보여주자면 이렇다.
val src = Mat() //grayScale 이라고 가정
val dest = Mat()
for(rowIdx: Int in 0 until src.rows()) {
for(colIdx: Int in 0 until src.cols()){
var pixel = dest.get(rowIdx, colIdx)
dest.put(rowIdx, colIdx, pixel-100)
}
}
코드상으로 보았을 땐 별 문제 없이 깔끔해보이지만, 속도는 처참하다.
영역이 더 커질수록 더 느려진다. 왜그럴까??
그 이유는 라이브러리를 너무 많이 호출했기 때문이다.
라이브러리 호출이 많은 게 왜 느려질까??
자바 쪽에서는 JNI(Java Native Interface)라고 하여 JVM위에서 실행되는 코드가
다른 언어들로 작성된 라이브러리를 호출할 때 사용되는 인터페이스가 있다.
위의 절차대로 안드로이드에서 OpenCV를 호출한다는 것은
Android -> JNI -> OpenCV 의 절차를 거친다는 것이다.
픽셀 단위로 적용하면 라이브러리를 호출 횟수는 Row * Col 이 되면서 무수히 많은 전달과정이 생기며 느려진다.
그렇다면 위의 코드는 꼭 OpenCV를 써야만 할까??
openCV의 Mat을 선언할 때 내부동작을 이해해보면
- Row * Col 크기의 바이트를 할당하고
- put, get과 같은 메소드를 제공하는 것으로,
- 덕분에 한줄의 코드로 인해 코드 길이가 줄었고,
- 사람이 이해하는 행렬 방식으로 직관적이고 편리하게 작업할 수 있도록 도와준다.
하지만 위의 코드를 기준으로 핵심 로직은 픽셀마다 간단한 산술연산만 해주면 되는 작업인데,
굳이 openCV의 Mat 타입으로 변경해가면서 오버헤드를 가져갈 필요가 없고,
내부 동작 부분을 똑같이 Android 상에서 처리할 수 있다.
val src = Mat() //GRAY 예시(8UC1)
val srcValues = IntArray(src.total().toInt())
//srcValues 에 복사
// srcValues에서 계산
for(rowIdx: Int in 0 until src.rows()) {
for(colIdx: Int in 0 until src.cols()){
srcValues[rowIdx*src.cols() + colIdx] -= 100
}
}
src.put(0,0, srcValues)
이런 방식으로 보면 내용상으로는 같지만 openCV는 불러올 때와 다시 할당할 때만 사용하여
과도한 openCV 호출을 방지해 오버헤드를 줄일 수 있다.
이렇게 간단한 예시를 들었지만,
openCV 라이브러리를 과하게 호출되는 것 같다면
오버헤드를 덜어내기 위해 네이티브 코드를 사용하자는 게 핵심이다.
다른 방법으로는 CUDA와 같은 GPU 기반의 병렬처리를 이용하는 것도 방법도 있긴 하다.
결론
openCV 라이브러리를 적당히 쓰자 (알잘딱깔센)
참고
https://velog.io/@vrooming13/JNI-JAVA-Native-Interface
https://developer.android.com/training/articles/perf-jni?hl=ko
http://mcs.csueastbay.edu/~grewe/CS6825/Mat/OpenCV/speed/speed.html
'Framework > OpenCV' 카테고리의 다른 글
[3D LUT] 에 대해 이해한 내용 적어보기 (0) | 2023.08.14 |
---|---|
[OpenCV] C++ Mat 채널 변경 (permute, transpose) (0) | 2021.12.08 |
OpenCV 3.x 4.x 차이 (0) | 2021.11.19 |
VIDEOIO(CV_IMAGES): raised OpenCV exception 에러 (0) | 2021.08.25 |
ImportError: libGL.so.1: cannot open shared object file: No such file or directory (0) | 2021.07.30 |