2012년 12월 28일 금요일

OpenCV #2-5 Example (이웃 접근으로 영상 조회 - 부연 설명)

 - 이웃 화소에 대한 계산이 끝나면 대부분 커널 행렬을 표시.
 - 다음 예제에서 사용한 선명한 필터에서 커널은 다음과 같다.
 0  -1  0
-1  5  -1
 0  -1  0

 - 따로 명시되지 않으면 현재 화소는 커널의 중심에 대응.
 - 커널 내 각 셀에 있는 값은 일치하는 화소와 곱하는 인자를 의미.
 - 화소에 대한 커널을 적용한 결과는 모든 곱셈의 합. 커널의 크기는 이웃 크기에 대응.
 - 선명화 필터에서 볼 수 있듯 현재 화소의 네 가지 수평과 수직 이웃은 -1로 곱하는데, 가운데는 5를 곱한다.

 - 필터링은 영상처리에서 일반적인 작업이므로, OpenCV는 cv::filter2D함수로 해당 작업을 수행한다. - 선명화 함수 (사용하기 위해서는 커널 - 행렬 형태로 정의해야 함)

  • Example

#include <iostream>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp> // filter2D를 사용하기 위해 include

void sharpen2D(const cv::Mat &image, cv::Mat &result){
cv::Mat kernel(3, 3, CV_32F, cv::Scalar(0));
// 커널 생성(모든 값을 0으로 초기화)
kernel.at<float>(1, 1) = 5.0; // 커널 값에 할당
kernel.at<float>(0, 1) = -1.0;
kernel.at<float>(2, 1) = -1.0;
kernel.at<float>(1, 0) = -1.0;
kernel.at<float>(1, 2) = -1.0;

cv::filter2D(image, result, image.depth(), kernel);
// 영상 필터링
}

int main()
{
cv::Mat image = cv::imread("img2.jpg", 0);
cv::Mat result;

sharpen2D(image, result);

cv::namedWindow("Image");
cv::imshow("Image", image);

cv::namedWindow("Result");
cv::imshow("Result", result);

cv::waitKey(5000);

return 0;
}

  • Result



  • 참고문헌 : OpenCV 2 Computer Vision Application Programming Cookbook

OpenCV #2-5 Example (이웃 접근으로 영상 조회)

 - 라플라시안 연산자 (선명한 연산자)
 - 영상에 라플라시간을 거친 결과를 빼면 영상 에지가 두드러지게 돼 선명한 영상이 됨.
 - sharpened_pixel = 5*current-left-right-up-down;
 - current는 가운데, left는 바로 현재 한 행의 왼쪽의 화소, up은 이전 줄의 가운데에 대응 하는 화소이다.

  • Example

#include <iostream>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>

void sharpen(const cv::Mat &image, cv::Mat &result){
result.create(image.size(), image.type());
// 필요하면 할당
// create는 원본의 사이즈와 타입을 똑같이 만들어줌

for(int j=1 ; j<image.rows-1 ; j++){ // 모든 행에 대해(처음과 마지막을 제외하고)
const uchar* previous = image.ptr<const uchar>(j-1); // 이전 행
const uchar* current = image.ptr<const uchar>(j); // 현재 행
const uchar* next = image.ptr<const uchar>(j+1); // 다음 행
uchar* output = result.ptr<uchar>(j); // 결과 행

for(int i=1 ; i<image.cols-1 ; i++){
*output++ = cv::saturate_cast<uchar>(5*current[i] + current[i-1] - current[i+1] - previous[i] - next[i]);
output[i]= cv::saturate_cast<uchar>(5*current[i]-current[i-1]-current[i+1]-previous[i]-next[i]);
}
}
// 처리하지 않는 화소를 0으로 설정
result.row(0).setTo(cv::Scalar(0));
result.row(result.rows-1).setTo(cv::Scalar(0));
result.col(0).setTo(cv::Scalar(0));
result.col(result.cols-1).setTo(cv::Scalar(0));
}

int main()
{
cv::Mat image = cv::imread("img2.jpg", 0);
cv::Mat result;

sharpen(image, result);

cv::namedWindow("Image");
cv::imshow("Image", image);

cv::namedWindow("Result");
cv::imshow("Result", result);

cv::waitKey(5000);

return 0;
}

  • Result



  •  예제 분석

 - 이전 행과 다음 행의 이웃 화소에 접근하려면 다른 하나에 대해서 간단히 포인터를 추가해 정의하며 서로 결합해 증가한다.
 - cv::staturate_cast는 0이하 255미만이라는 화소값 허용 범위를 벗어나는 경우가 종종 있다.
 - cv::staturate_cast<uchar>함수를 이용해서 음수 값을 0으로, 255 이상 값을 255로 변환한다.

 - 이웃을 완전히 정의하지 않았기 때문에 테두리 화소를 처리할 수 없어서 별도로 다뤄야하며, 여기서는 0으로 설정한다.
 - row와 col 메소드를 사용해 테두리 화소를 0으로 설정.
 - setTo : 모든 행렬 요소에 대한 값을 할당.
 - result.row(0).setTo(cv::Scalar(0)); // 영상의 첫 번째 행에 있는 모든 화소에 0을 할당
 - 3채널 컬러 영상이면 각 채널 화소에 할당하는 세 가지 값을 지정하는 cv::Scalar(a, b, c) 사용.

  • 참고문헌 : OpenCV 2 Computer Vision Application Programming Cookbook

OpenCV #2-4 Example (영상을 조회하기 위한 효율적인 반복문)

  • Example (함수나 코드의 실행시간 측정)

#include <iostream>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>

void colorReduce(cv::Mat &image, int div=64) {


 int nl= image.rows; // 행 개수

 int nc= image.cols; // 열 개수
              
      for (int j=0; j<nl; j++) {
          for (int i=0; i<nc; i++) {
            // 각 화소에 대한 처리
                  image.at<cv::Vec3b>(j,i)[0]= image.at<cv::Vec3b>(j,i)[0]/div*div + div/2;
                  image.at<cv::Vec3b>(j,i)[1]= image.at<cv::Vec3b>(j,i)[1]/div*div + div/2;
                  image.at<cv::Vec3b>(j,i)[2]= image.at<cv::Vec3b>(j,i)[2]/div*div + div/2;
            // 각 화소 처리 끝 ----------------
            } // 행 끝                   
      }
}

int main()

{
cv::Mat image = cv::imread("boldt.jpg");

double duration;

// 함수나 코드의 실행시간을 측정하는데 cv::getTickCount() 사용
duration = static_cast<double>(cv::getTickCount());
// cv::getTickCount() : 컴퓨터를 시작한 후부터 마지막 시간에 발생하는 클록 사이틀 수
std::cout << "duration : " << duration << std::endl;

colorReduce(image);


duration = static_cast<double>(cv::getTickCount())-duration;

std::cout << "getTickCount()-duration : " << duration << std::endl;
duration /= cv::getTickFrequency();
std::cout << "getTickFrequency() : " << duration << std::endl;
// cv::getTickFrequency() : 밀리초 단위로 코드 부분의 실행시간 알아냄
// ms 단위인 경과 시간

cv::namedWindow("Image");

cv::imshow("Image", image);
cv::waitKey(5000);

return 0;

}


  • Result



  • 예제 분석

 - 컬러 감축 공식은 다음과 같이 작성한다(아주 빠른 버전이다).

 - 소스

#include <iostream>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>

void colorReduce(cv::Mat &image, int div=64) {

 int nl= image.rows; // 행 개수
 int nc= image.cols; // 열 개수

 if(image.isContinuous()){ // 연속된 영상인가.
 nc = nc * nl ; // 채울 화소가 없다면...
 nl = 1; // 지금은 1차원 배열
 }

 int n = static_cast<int>(log(static_cast<double>(div))/log(2.0));
// 화소값을 반올림할 때 사용하는 마스크
 uchar mask = 0xFF<<n; // 예를 들어 div=16, mask=0xF0 
              
      for (int j=0; j<nl; j++) { // 모든 화소를 대상으로
 uchar* data = image.ptr<uchar>(j);
          for (int i=0; i<nc; i++) {
            // 각 화소에 대한 처리
                  *data++= *data&mask + div/2;
 *data++= *data&mask + div/2;
 *data++= *data&mask + div/2;
            // 각 화소 처리 끝 ----------------
            } // 행 끝                   
      }
}

int main()
{
cv::Mat image = cv::imread("boldt.jpg");

colorReduce(image);

cv::namedWindow("Image");
cv::imshow("Image", image);
cv::waitKey(5000);

return 0;
}


  • 참고문헌 : OpenCV 2 Computer Vision Application Programming Cookbook

OpenCV #2-3 Example (반복자로 영상 조회)

  • Example (반복자 - 컬렉션의 각 요소를 조회하기 위한 전문 클래스)

#include <iostream>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>

void colorReduce(cv::Mat &image, int div=64){
// 밑줄은 템플릿 메소드.
// 반복자는 cv::MaIterator_<cv::Vec3b> it; 또는,
// cv::Mat_<cv::Vec3b>::iterator it; 으로 선언.
// 반복자는 반환 타입이 명시되어야 한다.
// 컬러 감축을 처리하기 때문에 cv:Vec3b를 반환.
cv::Mat_<cv::Vec3b>::iterator it= image.begin<cv::Vec3b>();
// 초기 위치에서 반복자 얻기
cv::Mat_<cv::Vec3b>::iterator itend= image.end<cv::Vec3b>();
// 마지막 위치에서 얻기

for ( ; it!= itend; ++it) { // 모든 화소를 조회
// 각 화소에 대한 처리
(*it)[0]= (*it)[0]/div*div + div/2; // 포인터 형태 캐스팅
(*it)[1]= (*it)[1]/div*div + div/2;
(*it)[2]= (*it)[2]/div*div + div/2;
// 각 화소 처리 끝
 }
}

int main()
{
cv::Mat image = cv::imread("boldt.jpg");

colorReduce(image);

cv::namedWindow("Image");
cv::imshow("Image", image);
cv::waitKey(5000);

return 0;
}


  • Result


  • 부연 설명

 - 반복자의 시작 위치와 마지막 위치는 begin과 end 템플릿 메소드로 얻는다.
 - begin과 end는 cv::Mat) 인스턴스에 대한 참조자를 사용해서 얻을 수 있다.
 - cv::Mat 참조자를 생성할 때 이미 지정됬기 때문에 begin과 end 메소드에서 반복자 타입을 명시하는 경우를 피할 수 있다.
 - cv::Mat_<cv::Vec3b> climage = image;
 - cv::Mat_<cv::Vec3b>::iterator it = climage.begin();
 - cv::Mat_<cv::Vec3b>::iterator itend = climage.end();


  • 참고문헌 : OpenCV 2 Computer Vision Application Programming Cookbook

OpenCV #2-2 Example (포인터로 영상 조회 - 부연 설명)

  • 다른 컬러 감축 공식

 - 다른 컬러 감축 공식은 정수로 나눈다는 장점이 있으며, 나눈 결과인 부동 소수점이 가장 가까운 정수가 된다.
 - 나머지 연산자를 사용해 컬러 감축을 계산할 수 있으며, div의 곱에 가까워 진다.
 - data[i] = data[i] - data[i]%div + div/2;

 - 소스
#include <iostream>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>

void colorReduce(cv::Mat &image, int div=64){
int nl = image.rows;
int nc = image.cols*image.channels();

for(int j=0 ; j<nl ; j++){
uchar* data = image.ptr<uchar>(j);

for(int i=0 ; i<nc ; i++){
// 각 화소 처리
int v= *data;
*data++= v - v%div + div/2;
// 각 화소 처리 끝
} // 행 끝
}
}

int main()
{
cv::Mat image = cv::imread("boldt.jpg");

colorReduce(image);

cv::namedWindow("Image");
cv::imshow("Image", image);
cv::waitKey(5000);

return 0;
}


 - 또 다른 컬러 감축 공식의 옵션 : 비트 연산자
 - 감축 인자를 2의 지수승, 즉 div=pow(2,n)으로 제한하면 화소값의 첫 번째 n 비트를 마스킹 할 때 가장 낮은 div의 곱에 가깝다.
 - 마스크는 간단한 비트 이동 연산으로 얻는다.

 - 소스
#include <iostream>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>

void colorReduce(cv::Mat &image, int div=64){
int nl = image.rows;
int nc = image.cols*image.channels();
int n= static_cast<int>(log(static_cast<double>(div))/log(2.0));
 // 화소값을 반올림하기 위해 사용하는 마스크
uchar mask= 0xFF<<n; // 예, div=16이면 mask= 0xF0
              

for(int j=0 ; j<nl ; j++){
uchar* data = image.ptr<uchar>(j);

for(int i=0 ; i<nc ; i++){
// 각 화소 처리
*data++= *data&mask + div/2;
// 각 화소 처리 끝
} // 행 끝
}
}

int main()
{
cv::Mat image = cv::imread("boldt.jpg");

colorReduce(image);

cv::namedWindow("Image");
cv::imshow("Image", image);
cv::waitKey(5000);

return 0;
}

  • 입출력 인자 사용

 - 입력 영상에 직접 적용, 내부 공간에서 변환한다. (다른 메모리에 저장)
 - 영상의 복사본을 강제로 만듬으로 써 원 영상을 지킬 수 있다.
 - clone 메소드를 사용해서 영상과 동일한 깊은 복사본을 만든다.

 - 소스
#include <iostream>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>

void colorReduce(cv::Mat &image, int div=64){
int nl = image.rows;
int nc = image.cols*image.channels();

for(int j=0 ; j<nl ; j++){
uchar* data = image.ptr<uchar>(j);

for(int i=0 ; i<nc ; i++){
// 각 화소 처리
data[i] = data[i]/div*div + div/2;
// 각 화소 처리 끝
} // 행 끝
}
}

int main()
{
cv::Mat image;
image = cv::imread("boldt.jpg"); // 영상 읽기
cv::Mat imageClone = image.clone(); // 영상 복제
colorReduce(imageClone); // 복제본 처리, 원래 영상을 건드리지 않음

cv::namedWindow("Image Result");
cv::imshow("Image Result", imageClone);
cv::waitKey(5000);

return 0;
}


 - 사용자에게 재사용 옵션을 제공하거나 내부 공간 처리를 사용하지 않는 함수를 정의하는 방법은 따로 오버로딩을 하는 방법이 있음.

 - 소스
#include <iostream>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>

void colorReduce(cv::Mat &image, cv::Mat &result, int div=64){
// &image는 입력 영상, &result는 결과 영상
int nl = image.rows;
int nc = image.cols*image.channels();
result.create(image.rows, image.cols, image.type());
// 결과 영상이 입력 영상과 일치하는 크기와 화소 타입을 할당한 데이터 버퍼를 갖는지 확인
// create는 행렬에 새로운 크기와 타입을 갖고 다시 할당할 때 사용

for(int j=0 ; j<nl ; j++){
const uchar* data_in = image.ptr<uchar>(j);
uchar* data_out = result.ptr<uchar>(j);

for(int i=0 ; i<nc ; i++){
// 각 화소 처리
data_out[i] = data_in[i]/div*div + div/2;
// 각 화소 처리 끝
} // 행 끝
}
}

int main()
{
cv::Mat image;
image = cv::imread("boldt.jpg"); // 영상 읽기

colorReduce(image, image); 
// 입력 영상을 const 참조자로 넘김
// 함수에서 입력 영상을 수정하지 않음을 의미
// 내부 공간 처리를 선호한다면 동일한 영상을 입력과 결과로 지정

cv::Mat result;
colorReduce(image, result);
// 입력 영상과 결과 영상이 동일하면 수행

cv::namedWindow("Image Result");
cv::imshow("Image Result", result);
cv::waitKey(5000);

return 0;
}

  • 연속된 영상을 효율적으로 조회

 - 효율상의 이유로 영상 내 각 행의 마지막에 추가 화소로 채울 수 있다.
 - 하지만 연속된 영상은 채우지 않아도 된다.
 - 영상을 WxH 화소의 긴 1차원 배열로 볼 수 있다.
 - isContinues : 영상이 채워진 화소를 포함하지 않았을 땐 true 반환.

 -  소스
#include <iostream>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>

void colorReduce(cv::Mat &image, int div=64){
int nl = image.rows;
int nc = image.cols*image.channels();

if(image.isContinuous()){ // 채운 화소가 없을 경우
nc = nc*nl;
nl = 1; // 지금은 1차원 배열
} // 이 반복문은 단 한번 실행
// 연속된 영상의 경우이다.

for(int j=0 ; j<nl ; j++){
uchar* data = image.ptr<uchar>(j);

for(int i=0 ; i<nc ; i++){
// 각 화소 처리
data[i] = data[i]/div*div + div/2;
// 각 화소 처리 끝
} // 행 끝
}
}

int main()
{
cv::Mat image = cv::imread("boldt.jpg");

colorReduce(image);

cv::namedWindow("Image");
cv::imshow("Image", image);
cv::waitKey(5000);

return 0;
}


 - 위 코드는 연속성 테스트에서 채워진 화소를 포함하지 않는 영상임을 보임.
 - 너비를 1로 하고 높이를 WxH로 설정해 바깥쪽 반복문 제거.
 - reshape : 임의의 메모리 복사나 재할당 없이 행렬의 차원 수를 변경.
 - 내부 반복문은 여러개의 작은 영상을 같은 반복문에서 동시에 조회할 때 유리.

 - 소스
#include <iostream>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>

void colorReduce(cv::Mat &image, int div=64){
if(image.isContinuous()){ // 채운 화소가 없을 경우
image.reshape(1, image.cols*image.rows);
// 1은 채널의 새로운 개수, 다음 파라미터는 행의 새로운 값
// 행의 값에 맞춰 열 개수 조절
}
int nl = image.rows; // 행 개수
int nc = image.cols*image.channels();

for(int j=0 ; j<nl ; j++){
uchar* data = image.ptr<uchar>(j);

for(int i=0 ; i<nc ; i++){
// 각 화소 처리
data[i] = data[i]/div*div + div/2;
// 각 화소 처리 끝
} // 행 끝
}
}

int main()
{
cv::Mat image = cv::imread("boldt.jpg");

colorReduce(image);

cv::namedWindow("Image");
cv::imshow("Image", image);
cv::waitKey(5000);

return 0;
}

  • 저수준 포인터 산술 연산

 - unsigned char 포인터를 반환하는 데이터 속성에는 메모리 블록의 첫 번째 요소인 주소를 갖는다.
 - step : 해당 행의 바이트 총 개수(채운 화소를 포함)를 제공. 일반적으로 행 j와 열 i에 놓인 화소의 주소를 얻을 수 있다.
 - data=image.data + j*image.step + i*image.elemSize(); // (j, i)에 있는 화소의 주소를 &image.at(j, i)로 얻을 수 있다.

 - 소스
#include <iostream>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>

void colorReduce(cv::Mat &image, int div=64){
int nl = image.rows; // 행 개수
int nc = image.cols*image.channels();
int step = image.step; // 유효 너비
uchar *data= image.data; // 영상 버퍼에 대한 포인터 가져오기

for(int j=0 ; j<nl ; j++){
uchar* data = image.ptr<uchar>(j);

for(int i=0 ; i<nc ; i++){
// 각 화소 처리
data[i] = data[i]/div*div + div/2;
// 각 화소 처리 끝
} // 행 끝
data+= image.step; 
// 다음 줄(유효 너비를 사용해 행 포인터를 이동하면 하나씩 다음 행으로 이동)
}
}

int main()
{
cv::Mat image = cv::imread("boldt.jpg");

colorReduce(image);

cv::namedWindow("Image");
cv::imshow("Image", image);
cv::waitKey(5000);

return 0;
}


  • 참고문헌 : OpenCV 2 Computer Vision Application Programming Cookbook

OpenCV #2-2 Example (포인터로 영상 조회)

  • Example (Color reduce function - 분석 복잡도를 낮추기 위해 사용)

#include <iostream>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>

void colorReduce(cv::Mat &image, int div=64){ // div = 64는 color Reduce 방법 중 하나

int nl = image.rows; // 행 개수 (높이)
int nc = image.cols*image.channels();
// 각 행에 있는 원소의 총 개수. cols는 너비
// image.channels()이 1이면 그레이레벨, 3이면 컬러레벨 영상

for(int j=0 ; j<nl ; j++){

uchar* data = image.ptr<uchar>(j); // j열의 주소를 가져오기

for(int i=0 ; i<nc ; i++){

// 각 화소 처리
data[i] = data[i]/div*div + div/2;
//*data++= *data/div*div + div/2;
// 각 화소 처리 끝
} // 행 끝
}
}

int main()

{
cv::Mat image = cv::imread("boldt.jpg");

colorReduce(image);


cv::namedWindow("Image");

cv::imshow("Image", image);
cv::waitKey(5000);

return 0;

}


  • Result



  • 예제 분석

 - 영상 데이터 버퍼의 첫 3 바이트는 상위 왼쪽 화소의 3 채널 값, 다음 3 바이트는 첫 번째 행의 두 번째 화소값
 - 영상의 너비가 W이고 높이가 H라면 WxHx3 uchar에 대한 메모리 블록이 필요
 - 데이터 속성인 cols는 영상의 너비 (열의 개수), rows는 영상의 높이
 - step 데이터 속성은 바이트 개수인 유효 너비 (uchar가 아닌 다른 타입이라도 너비당 바이트 개수를 갖는다.)

 - elemSize : 화소의 크기 (3채널 short 정수 행렬 CV_16SC3이라면 6을 반환)
 - nchannels : 영상의 채널 개수 (그레이레벨 영상은 1, 컬러 영상은 3)
 - total : 행렬 내 화소(행렬 원소)의 총 개수

 - 너비 당 화소 개수 값 : int nc = image.cols * image.channels();

 - ptr : 포인터 산술 연산을 이용한 계산에서 vc:Mat 클래스는 직접 영상의 열인 주소를 가져오는 ptr 메소드를 제공.
 - j열의 주소를 반환하는 템플릿 메소드 : uchar* data = image.ptr<uchar>(j);

 - 행에서 행으로 옮기기 위한 포인터 산술 연산 : *data++ = *data/div*div + div/2

  • 참고문헌 : OpenCV 2 Computer Vision Application Programming Cookbook

OpenCV #2-1 Example (화소값에 접근)

OpenCV #2 화소 다루기

  • Example (Salt-and-pepper noise)

#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>

void salt(cv::Mat &image, int n){ 
// &image는 읽어들인 이미지
//n은 흰색 값으로 덮기 원하는 화소의 개수
for(int k=0 ; k<n ; k++){
int i = rand()%image.cols; // rand()는 MFC 난수 생성기
int j = rand()%image.rows; // cols와 rows로 나눈 나머지 수 사용

if(image.channels() == 1){ // 그레이레벨 영상
image.at<uchar>(j, i) = 255; // 255는 흰색. j, i를 흰색으로 바꿈.
}
else if(image.channels() == 3){ // 컬러 영상
image.at<cv::Vec3b>(j, i)[0] = 255;
image.at<cv::Vec3b>(j, i)[1] = 255;
image.at<cv::Vec3b>(j, i)[2] = 255;
}
}
}

int main()
{
srand(cv::getTickCount()); // 임의 숫자 생성기 초기화

cv::Mat image = cv::imread("boldt.jpg");
// cv::Mat image = cv::imread("boldt.jpg", 0); // 0이 들어가면 회색조

salt(image, 3000);

cv::namedWindow("Image");
cv::imshow("Image", image);
cv::imwrite("slated.bmp", image);
cv::waitKey(5000);

return 0;
}


  • Result



  • 예제 분석

 - public 멤버 변수인 cols와 rows는 영상의 행과 열

 - 각 원소에 접근 하려면 cv::Mat의 at(int x, int y) 메소드를 이용
 - cv::Mat은 임의 타입의 요소를 갖지만 반환 타입을 지정해야 한다.
 - at 메소드를 호출하면 영상 원소 타입을 image.at<uchar>(j, i)=255;로 지정해야 한다.

 - 특정 타입을 행렬 내 타입과 일치하는지 반드시 확인해야 한다.
 - at 메소드는 모든 종류의 타입 변환을 수행하지 않는다.
 - 컬러 영상을 갖는 cv::Mat는 세 8비트 값의 벡터를 반환한다.
 - OpenCV는 byte 백터인 cv::Vec3b 같은 정의된 타입을 제공한다.
 - cv::Vec3b는 세 개의 unsigned char인 벡터다.
 - image.at<cv::Vec3b>(j ,i)[channel] = value; // channel은 세 개 컬러 채널 중 하나
 - Vec3b의 b는 byte를 의미하고, short의 s, int의 i, float의 f, double의 d가 있다.
 - 템플릿 클래스 cv::Vec<T, N>에서 T는 타입, N은 백터 요소의 개수

  • 부연 설명

 - 행렬 타입을 알고 있다면 cv::Mat의 템플릿 하위 클래스인 cv::Mat_클래스를 사용할 수 있다.
 - 이 클래스는 새로운 데이터 속성이 없기 때문에 포인터나 참조자로 해당 클래스를 다른 클래스로 직접 변환한다.
 - 그러므로 image가 uchar 행렬을 참조하고 있다면 다음과 같이 쓴다.
 - cv::Mat_<uchar> im2 = image; // im2는 image를 참조
 - im2(50, 100) = 0; // 50번째 행, 100번째 열, 즉 (50, 100)에 있는 화소에 접근
 - 즉, cv::Mat_의 원소 타입은 변수를 만들 때 선언된다.


  • 참고문헌 : OpenCV 2 Computer Vision Application Programming Cookbook

OpenCV #1 Example (종합)

  • Example

#include <iostream>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>

// 영상을 생성하고 반환하는 함수
cv::Mat function() {

// 영상 생성
cv::Mat ima(240,320,CV_8U,cv::Scalar(100));
// 영상 반환
return ima;
}

int main() {

// 영상 생성
cv::Mat image;
// 영상 크기 출력
std::cout << "size: " << image.size().height << " , " << image.size().width << std::endl;
// 영상 열기
image=  cv::imread("img.jpg");
// 영상을 성공적으로 열었는지 확인
if (!image.data) {
// no image has been created?
return 0;
}
// 영상 크기 출력
    std::cout << "size (after reading): " << image.size().height << " , " << image.size().width << std::endl;

// 영상 띄워 보기
cv::namedWindow("Original Image"); // 창 정의
    cv::imshow("Original Image", image); // 영상 보기

// 다른 영상 생성
cv::Mat result;
// 영상 뒤집기
cv::flip(image,result,1); // 수평에 대한 양수
                              // 0은 수직,
                              // 모두 음수임
// 결과 띄워 보기
cv::namedWindow("Output Image");
cv::imshow("Output Image", result);
// 키 입력 기다리기
cv::waitKey(0);
// 파일에 영상 쓰기
cv::imwrite("output.bmp", result);

// 두 개의 새로운 영상 생성
cv::Mat image2, image3;

image2= result; // 동일한 데이터를 참조하는 두 영상
result.copyTo(image3); // 새로운 복사본이 생성됨

// 수직으로 뒤집기
cv::flip(result,result,0);

// 결과 띄워 보기
cv::namedWindow("image 2");
cv::imshow("image 2", image2);
cv::namedWindow("image 3");
cv::imshow("image 3", image3);

// 명암도 영상 가져오기
cv::Mat gray= function();
// 결과 띄워 보기
cv::namedWindow("Gray Image");
cv::imshow("Gray Image", gray);

// 키 입력 대기
cv::waitKey(0);
return 1;
}


  • Result












  • 참고문헌 : OpenCV 2 Computer Vision Application Programming Cookbook

2012년 12월 27일 목요일

OpenCV #1-4 Example (함수 사용)

  • Example

#include <iostream>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
using namespace std;

class Test{
cv::Mat ima;
public:
Test() : ima(240, 320, CV_8U, cv::Scalar(100)){}
cv::Mat method() {return ima; }
};

cv::Mat function()
{
cv::Mat ima(240, 320, CV_8U, cv::Scalar(100));
// 이미지 생성
// cv::Mat 객체에 대한 할당 모델은 다른 함수에서 안전하게 쓸 수 있다.
return ima; // 이미지 리턴
}

int main()
{
cv::Mat gray = function();
// main 함수에서 함수 호출.
// 그레이레벨 영상 갖기.

cv::namedWindow("gray");
cv::imshow("gray", gray);
cv::waitKey(0);

return 1;
}


  • Result


  • 참고문헌 : OpenCV 2 Computer Vision Application Programming Cookbook

OpenCV #1-3 Example (Mat, copyTo)

  • Example

#include <iostream>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
using namespace std;
// using namespace cv; // cv 네임스페이스 내부 정의

int main()
{
cv::Mat ima(240, 320, CV_8U, cv::Scalar(100));
// 기본적으로 0x0이지만 초기 크기 지정 가능.
// 1 바이트 화소의 경우 CV_8U를 지정. 
// U 문자는 부호 없음을 의미, S 문자는 부호있는 숫자 선언.
// 컬러 영상의 경우 3차원 채널(CV_8UC3)을 지정.
// cv::Scalar(100)은 0과 255가 아니므로 회색조.

cv::Mat image1;
image1 = cv::imread("img3.jpg");

cv::Mat image2, image3;
image2 = image1; // 같은 데이터를 참조하는 두 영상 (복사가 아닌 참조를 한다.)
image1.copyTo(image3); // copyTo를 이용해서 새로운 복사본 생성

flip(image1, image1, 1); 
// image3는 영상의 복사본을 갖고 있으므로 변하지 않는다.
// image1과 2는 변환에 의한 결과를 보게 된다.

cv::namedWindow("image1");
cv::imshow("image1", image1);
cv::namedWindow("image2");
cv::imshow("image2", image2);
cv::namedWindow("image3");
cv::imshow("image3", image3);
cv::waitKey(0);

return 1;
}

  • Result





  • 참고문헌 : OpenCV 2 Computer Vision Application Programming Cookbook