-
[The Core Functionality] How to scan Images, Lookup tables with Open CVComputer Vision 2020. 7. 25. 18:04
https://docs.opencv.org/master/db/da5/tutorial_how_to_scan_images.html
Matrix Item을 저장하기 위해 unsigned char C 및 C++ 타입을 사용함으로써, 픽셀의 채널은 최대 256개의 다른 값을 가질 수 있다.
3채널 이미지의 경우 너무 많은 색상을 형성할 수 있다. ( 정확히 1600만 )
=> 너무 많은 색조를 사용하면 알고리즘 성능이 크게 저하될 수 있다.
=> 때론, 동일한 최종 결과를 얻기 위해 훨씬 적은 데이터 크기로 작업하는 것으로 충분하다.
-> 이를 위해서 색상 공간을 줄이는 것이 일반적이다.
: 색상 공간 현재값을 새로운 입력 값으로 나누면 더 적은 색상으로 끝난다.
Ex) 0~9 값은 0, 10~19는 10으로 초기화.
간단한 Color space reduction algorithm은 이미지 매트릭스의 모든 픽셀을 통과하고 공식을 적용한다.
하지만 이미지의 크기가 커질 수록 이러한 연산은 불가능해 진다.
따라서 더 큰 이미지의 경우 가능한 모든 값을 미리 계산하고 할당하는 동안 Lookup table 을 사용하여 할당 하는 것이 좋다.
Lookup Table은 주어진 입력값 변형에 대해 최종 출력 값을 유지해주는 배열이다.
Image Matrix는 어떻게 메모리에 저장될까.
매트릭스의 크기는 사용된 색상 시스템에 따라 다르다. 보다 정확하게는 사용되는 채널수에 따라 다르다.
회색조 이미지의 경우 다음과 같다.
다중 채널 이미지의 경우 열에는 채널 수만큼 많은 하위 열이 포함된다.
BGR 컬러 시스템의 경우
채널 순서는 RGB가 아니라 BGR이다.
많은 경우 메모리는 연속적인 방식으로 행을 저장하기에 충분히 크기 때문에 행이 하나씩 따라가면서 하나의 긴 행을 작성할 수 있다.
The Efficient Way
성능면에서는 클래식 C 스타일 연산자 [] ( Pointer ) 액세스를 이길 수 없다.
기본적으로 행의 시작에 대한 포인터를 획득하고 끝날때 까지 통과한다.
행렬이 연속적인 방식으로 저장되는 특별한 경우에는 포인터를 한번만 요청하고 끝까지 가면 된다.
컬러 이미지의 경우 3개의 채널이 있으므로 각 행에서 3배 더 많은 항목을 통과해야한다.
The Core Funtion
이것은 Lookup Table을 수정하는 보너스 방법이다. 이미지 처리에서 주어진 이미지 값을 모두 다른 값으ㅗ 수정하는 것이 일반적이다.
Open CV 에서는 이미지의 스캔 로직을 작성할 필요 없이 이미지 값을 수정하는 기능을 제공한다.
Core moudle의 cv::LUT() 함수를 사용한다.
위의 방법들을 구현해 보기.
#include <iostream> #include <opencv2/core.hpp> #include <opencv2/core/utility.hpp> #include <opencv2/highgui/highgui.hpp> #include "opencv2/imgcodecs.hpp" using namespace std; using namespace cv; Mat& ScanImageandReduceC(Mat& I, const uchar * table); int main() { Mat img, J; img = imread("chemnitz.jpg",IMREAD_COLOR); if(!img.data){ cout<<"Could not open or find the image"<<endl; return -1; } int divideWith = 50; const int times = 100; double t; uchar table[256]; for(int i=0; i<256; ++i) table[i] = (uchar)(divideWith * (i/divideWith)); t = (double)getTickCount(); for(int i=0; i<times; ++i){ Mat clone_i = img.clone(); J = ScanImageandReduceC(clone_i, table); } t = 1000*((double)getTickCount()-t)/getTickFrequency(); t /= times; cout<<"Time of reducing with the C operator [] (average for "<<times<<" runs) : "<<t<<" miliseconds."<<endl; Mat lookUpTable(1, 256, CV_8U); uchar* p = lookUpTable.ptr(); for(int i=0; i<256; ++i) p[i] = table[i];//! [table-init] t = (double) getTickCount(); for(int i=0; i<times; ++i) LUT(img, lookUpTable, J); t = 1000*((double)getTickCount()-t)/getTickFrequency(); t /= times; cout<<"Time of reducing with the LUT function (averaged for"<<times<<" runs ): "<<t<<" milliseconds."<<endl; namedWindow("Chemnitz", WINDOW_AUTOSIZE); imshow("Chemnitz", img); waitKey(0); return 0; } Mat& ScanImageandReduceC(Mat& I, const uchar * table){ CV_Assert(I.depth() == CV_8U); int channels = I.channels(); int nRows = I.rows; int nCols = I.cols * channels; if(I.isContinuous()){ nCols *= nRows; nRows = 1; } int i,j; uchar* p; for(i=0; i<nRows; ++i){ p = I.ptr<uchar>(i); for(j=0; j<nCols; ++j){ p[j] = table[p[j]]; } } return I; }
가장 빠른 방법은 LUT 기능이다. Open CV 라이브러리는 Intel Threaded Building Blocks 를 통해 다중 스레드를 지원하기 때문이다.
그러나 간단한 이미지 스캔을 작성해야 하는 경우는 Pointer Method를 사용하는 것이 좋다.
'Computer Vision' 카테고리의 다른 글
[The Core Functionality] Operations with Images (0) 2020.07.25 [The Core Functionality] Mask Operations on Matices (0) 2020.07.25 [Introduction to OpenCV] Load, Modify, and Save an Image (0) 2020.07.25 [ Introduction to OpenCV ] Load and Display an Image (0) 2020.07.25 [The Core Functionality] Mat - The Basic Image Container (0) 2020.07.25