안녕하세요. 여러분들이 OpenCV를 이용하는 이유는 영상 및 비디오를 처리하는 모듈을 개발하기 위함일텐데요. 그렇다면 가장 기본이 되는 이미지 및 비디오를 데이터를 읽어오고 윈도우 화면에 출력해보는 시간을 갖도록 하죠.
1. OpenCV의 기본 자료형인 Mat 클래스
우리가 흔히 보는 화면은 2차원의 LED 배열로부터 화면의 색과 밝기를 표현합니다.
![]() |
그림 1. LED 디스플레이 array |
다들 휴대폰 화면을 가까이서 자세히 보면 이런 형태의 모양이 보일겁니다. TV를 가까이서 보신 분들은 꽤 익숙하실겁니다.
우리는 이처럼 평면상에 점을 찍어서 하나의 Pixel(이미지의 최소 단위)을 표현을 하고 그 Pixel들을 모아 이미지를 만들어내죠. 그런데 그 크기가 매우 작기때문에 우리 눈에는 이러한 복잡하고 불연속적인 이미지가 보이는 것이 아니라 하나의 연속적인 그림으로 보이게 되는거죠.
(인간의 눈은 High frequency 성분을 구분할 수 있는 능력이 떨어지기 때문입니다 이와 관련된 영상신호처리 이론은 추후에 올릴예정입니다)
따라서 OpenCV도 화면에 이미지를 나타낼 때 값들이 모여있는 형태의 Matrix(행렬) 자료형이 필요한겁니다.
![]() |
그림 2. 3D 좌표계에 표현된 RGB 색공간 |
Mat 클래스는 이러한 영상들을 간단하게 저장할 수 있도록 만들어 줍니다. Mat 오브젝트를 만드는 가장 손쉬운 방법은 생성자를 이용한 방법입니다.
Mat img(nrows, ncols, type, fillValue);
이런 형태로 선언하면 Mat 오브젝트를 생성할 수 있습니다. nrows는 행, ncols는 열, tpye은 이미지의 데이터 타입, fillValue는 이미지에 채우고 싶은 값입니다.
예시로 설명드리면
위 코드의 의미는
256(nrows) x 256(ncols) 크기의 이미지인데 픽셀당 Unsigned 8 bit integer(CV_8U)를 사용하여 스칼라 값인 255(Scalar(255))를 표현하겠다.
위 그림을 실행 시키면 화면에 아래와 같이 표현됩니다.
예시로 설명드리면
Mat img(256, 256, CV_8U, Scalar(255));
위 코드의 의미는
256(nrows) x 256(ncols) 크기의 이미지인데 픽셀당 Unsigned 8 bit integer(CV_8U)를 사용하여 스칼라 값인 255(Scalar(255))를 표현하겠다.
위 그림을 실행 시키면 화면에 아래와 같이 표현됩니다.
이미지의 데이터 타입에 들어가는 형태를 설명드리면
CV_<bit 수>{U or S or F}C<채널수>
- CV -> Computer Vision
- bit 수 -> 픽셀이 표현하는 데이터의 비트 수
- U -> Unsigned (-부호가 없는 0을 포함한 자연수를 의미)
- S -> Signed(-포함한 정수를 의미)
- F -> Floating point(실수형 표현을 의미)
- 채널 -> 이미지의 채널 수를 의미
정의는 아래와 같이 되어있습니다.
1
|
enum {CV_8U=0, CV_8S=1, CV_16U=2,CV_16S=3, CV_32S=4, CV_32F=5, CV_64F=6};
| cs |
지난 시간에 사용하였던 예제코드를 다시보면 이제는 이해가 가실겁니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
#include <opencv2/opencv.hpp>
#include <iostream>
using namespace cv;
using namespace std;
int main(int argc, char *argv[])
{
Mat img(256, 256, CV_8U, Scalar(255));
namedWindow("Mat img", WINDOW_AUTOSIZE);
imshow("Mat img", img);
waitKey(0);
return 0;
}
| cs |
한마디로 요약하면
256x256 unsigned int8의 이미지에 255값을 채운 윈도우를 띄워라 입니다.
2. 이미지 파일 로드하기
이에 따라 OpenCV는 다양한 이미지 파일 포멧을 Mat 형태로 저장할 수 있는 함수가 존재합니다.
기본 지원 파일 포멧
|
추가 파일 포멧
|
*.bmp, *.dib
*.pbm, *.pgm, *.ppm
*.sr, *.ras
|
*.jpeg, *.jpg, *.jpe
*.jp2
*.png
*.tiff, *.tif
*.webp
|
표 1. 사용 가능한 파일 포멧
이미지를 불러오는 함수는 다음과 같습니다.
1
|
Mat imread(const String& filename, int flags = IMREAD_COLOR )
| cs |
filename에 파일의 이름을 넣어주고 flags에는 이미지 변환 코덱에 주는 옵션이라고 생각하시면 됩니다.
옵션의 종류는 아래와 같습니다.
1
2
3
4
5
6
7
|
enum { IMREAD_UNCHANGED = -1, // 8bit, color or not
IMREAD_GRAYSCALE = 0, // 8bit, gray
IMREAD_COLOR = 1, // unchanged depth, color
IMREAD_ANYDEPTH = 2, // any depth, unchanged color
IMREAD_ANYCOLOR = 4, // unchanged depth, any color
IMREAD_LOAD_GDAL = 8 // Use gdal driver
};
| cs |
자신이 원하는 포멧을 정하고 파일을 읽으시면 됩니다.
이를 이용한 컬러 이미지를 읽고 흑백 이미지로 변환하여 파일로 출력하는 예제코드를 작성해보면
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
|
#include <opencv2/opencv.hpp>
#include <iostream>
using namespace cv;
using namespace std;
int main(int argc, char *argv[])
{
Mat in, out;
in = imread("lenna.png", IMREAD_UNCHANGED);
if (in.empty())
{
cout << "File read error\n";
return -1;
}
namedWindow("lenna.png", WINDOW_AUTOSIZE);
namedWindow("lenna_gray.png", WINDOW_AUTOSIZE);
imshow("lenna.png", in);
cvtColor(in, out, COLOR_BGR2GRAY);
imshow("lenna_gray.png", out);
waitKey(0);
imwrite("lenna_gray.png", out);
return 0;
}
| cs |
강의에서 사용하는 모든 사진자료는 제 블로그 github에서 받으실 수 있습니다.
https://github.com/smail91/blogspot_repo/tree/master/TC
그림 4. 코드 실행 후 화면 |
empty()함수는 Mat의 멤버 함수로 빈 이미지일 경우 true를 반환합니다.
namedWindow("lenna.png", WINDOW_AUTOSIZE);
이 함수는 이름 그대로 윈도우에 띄울 창의 이름과 크기를 설정하는 함수입니다. 그냥 간단하게 autosize로 설정하면 이미지의 크기만큼 윈도우가 생성되어 디스플레이됩니다.
imshow("lenna.png", in);
이 함수도 역시 image를 show한다. 즉 위에서 생성한 윈도우 창 lenna.png 창에 in이라는 이미지를 출력하라는 함수입니다.
cvtColor(in, out, COLOR_BGR2GRAY);
이 함수는 convert color로 in의 color image를 gray scale로 변환하여 out에 넣어라 라는 함수입니다. 주로 color format 변환에 쓰입니다.
waitKey(0);
이 함수는 키 입력 이벤트가 일어나기 전까지 대기하여 우리가 영상을 확인할 수 있습니다. 0 대신 다른 delay값을 넣어서 시간을 조절할 수 있습니다.(ms 단위)
imwrite("lenna_gray.png", out);
이 함수는 lenna_gray.png라는 이름으로 out이미지를 파일로 출력하라는 의미입니다.
여기까지 성공하셨으면 여러분은 OpenCV 왕초보는 벗어나신겁니다. ㅎㅎ
조금 더 힘내서 비디오 캡처까지 따라가봅시다!
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>
#include <iostream>
using namespace cv;
using namespace std;
int main(int argc, char *argv[])
{
Mat in_frame;
VideoCapture inVid(0);
if (!inVid.isOpened())
{
cout << "Camera error\n";
return -1;
}
namedWindow("VideoCapture", WINDOW_AUTOSIZE);
while (true)
{
inVid >> in_frame;
imshow("VideoCapture", in_frame);
if (waitKey(1000 / 30) >= 0) break;
}
inVid.release();
return 0;
}
| cs |
그림 5. 실행화면 (캠이 없어서 버추얼 아웃풋이 나오네요..) |
VideoCapture 클래스를 통해 웹캠을 읽어와서 (1000/30)밀리초마다 한번씩 이미지를 갱신해주는 코드입니다.
결국 video도 이미지를 짧은 시간동안 여러장(보통 1초에 30장 -> 30 FPS) 보여주어 움직이는 것처럼 보이게 만들기 때문에 위에 이미지를 읽는 코드와 거의 유사합니다.
여러분은 이 기본 코드를 이용하면 다양한 영상처리 프로그램을 제작하는 뼈대를 만드실 수 있을겁니다.
4. 참고문헌
[1] Learning Image Processing with OpenCV
[2] 위키피디아 LED array, RGB 색공간 그림