Programming/Opencv(C++, MFC)

[OPENCV 2.4.11, C++ Console] 두 장의 이미지 연결하기 예제

MVP 2020. 11. 27. 11:48

0. 개발 환경

Opencv 2.4.11

Visual Studio 2010

C++ Console Application

멀티바이트 문자 집합 사용

 

1. 참조

newpower.tistory.com/106

 

OpenCV 함수 정리

IplImage 구조체 멤버명 ● nChannels : 영상의 픽셀 당 채널 수를 나타내며 1~4 값이며 흑배영상의 채널의 수는 1이고 RGB 컬러영상의 채널의 수는 3이고 RGB 컬러순서는 다음과 같다. b0 g0

newpower.tistory.com

 

2. 개요

두 장의 이미지를 수직으로 연결하여 한 장의 이미지로 출력해보는 간단한 예제이다.

 

3. 예제 코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#include <opencv2/opencv.hpp>
using namespace cv;
 
int main()
{
    IplImage *src1 = cvLoadImage("apple.bmp");
    IplImage *src2 = cvLoadImage("banana.bmp");

    int ResultImageWidth = (src1->width > src2->width) ? src1->width : src2->width;
    int ResultImageHeight = src1->height + src2->height;
 
    IplImage* dst = cvCreateImage(cvSize(ResultImageWidth, ResultImageHeight), 
src1->depth, src1->nChannels);
    
    cvSetImageROI(dst, cvRect(00, src1->width, src1->height));
    cvCopy(src1, dst);
 
    cvSetImageROI(dst, cvRect(0, src1->height, src2->width, src2->height));
    cvCopy(src2, dst);
 
    cvResetImageROI(dst);
    cvNamedWindow("Merge", CV_WINDOW_AUTOSIZE);
    cvShowImage("Merge", dst);
    waitKey();
 
    return 0;
}
cs

 

4. 분석

더보기

    #include <opencv2/opencv.hpp>

-> opencv.hpp 헤더파일은 opencv에서 지원하는 모든 기능을 포함한다.

 

    using namespace cv;

-> cv 네임스페이스를 사용하도록 선언해 이후 작성되는 소스 코드에서 cv::를 생략할 수 있다.

 

    IplImage *src1 = cvLoadImage("apple.bmp");

    IplImage *src2 = cvLoadImage("banana.bmp");

-> IplImage 구조체

IplImage(intel Image Processing Library) 구조체는 Intel 이미지 처리 라이브러리에서 가져온 것이며 ImageData라는 배열에 영상의 픽셀 정보를 저장한다. IplImage 구조체는 다음의 멤버들을 포함한다.

여기서 주요한 멤버들을 살펴보자면 width, height, nChannels, depth, imageData, widthStep, origin이 있다.

width, height 멤버는 영상의 가로, 세로 크기를 의미하며 단위는 픽셀(pixel)이다.

nChannels 멤버는 색상 평면(Color plane)의 개수를 의미한다. 사용되는 채널은 grayscale = 1, truecolor = 3을 의미한다.

Depth 멤버는 영상 데이터를 저장하기 위해 사용되는 비트 수를 의미한다. depth는 다음 표와 같이 데이터를 몇 비트로 표현할 것인가를 나타낸다.

opencv에서 depth를 나타내는 상수 표, 출처 : https://newpower.tistory.com/106

imageData 멤버는 영상의 픽셀 정보를 저장하는 배열이다. 256 * 256 흑백 영상의 경우 첫 픽셀의 데이터가 imageData[0]에 들어가고 순서대로 imageData[65535]까지 들어간다.

컬러 영상의 경우 RGB의 역순인 B G R 순서에 따라 영상 데이터가 저장된다.

widthStep 멤버는 영상의 가로 크기를 나타내며 단위는 바이트(byte)이다. widthStep은 width(pixel) * nChannels의 결과값이며 8비트 1 채널(grayscale) 영상에서 영상의 가로 크기가 100 픽셀이면 widthStep은 10 바이트이고 8비트 3 채널(truecolor) 영상에서 영상의 가로 크기가 100픽셀이면 widthstep은 300바이트가 된다.

 

origin 멤버는 영상의 원점을 나타낸다. 0은 top-left origin, 1은 bottom-left origin(window bitmap style)을 의미한다.

 

-> cvLoadImage 함수

    CVAPI(IplImage*) cvLoadImage( const char* filename, int iscolor                                  CV_DEFAULT(CV_LOAD_IMAGE_COLOR));

cvLoadImage 함수는 파일로부터 이미지를 읽는 기능을 한다. filename 인수로 사용된 "apple.bmp" 같이 경로가 없는 문자열은 프로젝트 폴더 내의 apple.bmp 파일의 이미지를 읽어오는 역할을 한다. 다른 경로의 이미지 파일을 읽어올때는 그 이미지 파일의 경로를 인수로 넣으면 된다. iscolor는 0일 경우 grayscale, 기본 값인 1일 경우 truecolor로 이미지를 읽어온다.

 

    int ResultImageWidth = (src1->width > src2->width) ? src1->width : src2->width;

    int ResultImageHeight = src1->height + src2->height;
-> ResultImageWidth와 ResultImageHeight 정수형 변수들은 2장의 이미지를 이어 붙인 결과물인 이미지의 폭 길이와 세로 길이를 정하는 역할을 한다. 이때 ResultImageWidth 변수는 삼항 연산자를 이용하여 src1의 폭 길이가 더 길 경우 src1의 폭 길이를 변수에 저장하고 src2의 폭 길이가 더 길 경우 src2의 폭 길이를 변수에 저장한다. ResultImageHeight 변수는 src1의 세로 길이와 src2의 세로 길이를 더하여 저장한다.

 

    IplImage* dst = cvCreateImage(cvSize(ResultImageWidth, ResultImageHeight), 
    src1->depth, src1->nChannels);

-> cvCreateImage 함수

    CVAPI(IplImage*)  cvCreateImage( CvSize size, int depth, int channels );

cvCreateImage 함수는 새로운 영상을 만들기 위해 사용하는 함수이다.

이 함수의 첫번째 인수인 CvSize size 인수의 CvSize 구조체를 보면 정수형의 width, height를 멤버로 포함하고 있다. 새로운 영상 이미지의 폭 길이와 세로 길이를 지정하는 데 사용되는 인수라고 볼 수 있다. 여기서는 cvSize 인라인 함수를 이용하여 ResultImageWidth와 ResultImageHeight를 CvSize 인수로 넣어준다. 

두 번째 인수인 depth는 영상 데이터를 저장하는 데 사용되는 비트 수를 의미하고 세 번째 인수인 channels는 색상 평면의 개수의 개수를 의미한다. 여기서는 src1의 depth와 channels를 인수로 넘겼다.

 

    cvSetImageROI(dst, cvRect(00, src1->width, src1->height));

    cvCopy(src1, dst);

 

    cvSetImageROI(dst, cvRect(0, src1->height, src2->width, src2->height));

    cvCopy(src2, dst);

-> cvSetImageROI 함수

    CVAPI(void)  cvSetImageROI( IplImage* image, CvRect rect );

cvSetImageROI 함수는 영상 이미지의 관심 영역(Region of interest)을 사각형을 이용하여 설정하는 기능을 하는 함수이다. 여기서 관심 영역은 이미지에서 영상처리 작업을 진행할 일부 영역을 의미한다. 이 함수는 이 예제에서 굉장히 중요한 역할을 한다. 두 이미지를 연결하기 위해서는 관심 영역을 이용해야만 원하는 위치에 이미지를 배치할 수 있기 때문이다. 관심 영역을 설정하지 않거나 영역을 잘못 둘 경우 두 이미지가 서로 겹쳐 한 이미지만 보이는 등의 원치 않는 결과를 낳을 수 있기 때문이다.

이 함수의 첫번째 인수인 IplImage* image는 관심 영역을 설정할 이미지 헤더 포인터를 인수로 받으며 여기서 dst를 인수로 넘겼다.

두번째 인수인 CvRect rect는 관심 영역으로 설정할 사각형의 크기를 설정하는 인수이다. 여기서는 cvRect 인라인 함수를 이용하는데 이 cvRect 인라인 함수의 첫 번째 인수는 정수형 x 인수이며 사각형의 x 축 위치를 정하게 된다. 두 번째 인수는 정수형 y 인수이며 사각형의 y 축 위치를 정하게 된다. 세 번째 인수는 width 인수이며 사각형의 폭 길이, 네 번째 인수는 height 인수로 사각형의 세로 길이를 정하게 된다. 

-> cvCopy 함수

    CVAPI(void)  cvCopy( const CvArr* src, CvArr* dst,
                         const CvArr* mask CV_DEFAULT(NULL) );

cvCopy 함수는 A 배열을 B 배열로 복사하는 기능을 하는 함수이다. 여기서는 이미지를 복사하는데 쓰였다. 첫번째 인수가 src, 즉 A 배열이 될 것이며 두 번째 인수는 dst, B 배열이 된다.

 

여기서 cvSetImageROI, cvCopy 함수를 호출하지 않은 상태에서 dst 이미지를 출력하면 다음과 같이 될 것이다. 

위에서 cvCreateImage 함수를 이용하여 dst 이미지를 생성하고 이미지의 폭과 길이, depth와 channel은 설정되었으나 검은색의 배경만 덩그러니 출력된다.

이 상태에서 cvSetImageROI 함수를 이용하여 ROI를 설정한다. cvRect(00, src1->width, src1->height)는 (0,0) 좌표에서 src1의 폭 길이와 세로 길이만큼 다음 그림과 같이 관심영역이 설정된다. 다시 ROI를 설정하거나 Reset 하기 전까진 이후에 작동되는 모든 영상 처리는 다음의 관심 영역에서만 영향을 주게 된다. 

 

cvCopy로 이미지를 src1으로부터 복사하여 dst에 저장하고 출력해본다.

 

dst는 위와 같이 src1 이미지를 설정해둔 ROI 영역에서 src1의 폭과 길이만큼 출력하게 된다.

다음으로 cvSetImageROI 함수를 다시 호출하여 src1 이미지에 src2 이미지를 세로로 이어붙이기 위해서 ROI 영역을 src1 이미지가 끝나는 좌표에서 영역을 지정한다. src1 이미지의 세로 길이 다음에 바로 src2가 출력되도록 한다. 이 상태에서 출력해본다.

이렇게 두 이미지가 연결되어서 출력되는 것을 볼 수 있다.

    cvResetImageROI(dst);

-> cvResetImageROI 함수

이 함수는 이미지의 ROI와 COI를 Reset 시키는 함수이다. 관심 영역을 해제하기 전까지는 관심 영역 부분만 가지고 영상처리가 진행되므로 영상처리 작업이 끝났으면 이 함수를 호출하여 관심 영역을 해제시켜야 한다. 만약 관심 영역이 설정된 상태에서 해제를 하지 않고 출력 시 다음과 같이 관심 영역만 출력된다.

    cvNamedWindow("Merge", CV_WINDOW_AUTOSIZE);

-> cvNamedWindow 함수

     CVAPI(int) cvNamedWindow( const char* name, int flags CV_DEFAULT(CV_WINDOW_AUTOSIZE) );

이 함수는 window를 생성하는 함수이다. 첫번째 인수는 윈도우의 이름이 되며 두 번째 인수는 윈도우의 속성을 지정한다. 여기서 사용된 CV_WINDOW_AUTOSIZE(1)은 윈도우의 사이즈를 출력할 이미지에 맞게 자동으로 지정한다.

   

    cvShowImage("Merge", dst);

-> cvShowImage 함수

    CVAPI(void) cvShowImage( const char* name, const CvArr* image );

이 함수는 cvNamedWindow 함수에 의해 지정된 윈도우 내에서 이미지를 출력한다. 첫 번째 인수는 이름이 되며 두 번째 인수는 출력할 이미지를 받는다.

 

    waitKey();

-> waitKey 함수

    CV_EXPORTS_W int waitKey(int delay = 0);

이 함수는 키 입력 대기시간(millisecond 단위)을 인수로 받아서 대기시간만큼 대기하고 대기시간을 경과하거나 키 입력을 받을 시 종료한다. 키 입력 대기시간이 0이거나 비어있다면 키 입력을 받을 때까지 대기한다. 리턴 값은 키보드로 입력한 키 값(ASCII)이고 아무 키도 눌리지 않았다면 -1을 리턴한다.

 

 

 

 

5. 출력 결과