7.1 영상의 필터링 7.1.1 필터링 연산 방법 영상 처리에서 필터링(filtering)이란 영상에서 원하는 정보만 통과시키고 원치 않는 정보는 걸러 내는 작업입니다. 영상의 필터링은 보통 마스크(mask)라고 부르는 작은 크기의 행렬을 이용합니다. 마스크는 필터링의 성격을 정의하는 행렬이며 커널(kernel), 윈도우(window)라고도 부르며, 경우에 따라서는 마스크 자체를 필터라고 부르기도 합니다.
고정점은 현재 필터링 작업을 수행하고 있는 기준 픽셀 위치를 나타내고, 대부분의 경우 마스크 행렬 정중앙을 고정점으로 사용합니다.
(x,y) 좌표에서 마스크 연산을 통해 결과 영상의 픽셀 값g(x,y)를 구했으면, 다음에는 마스크를 한 픽셀 옆으로 이동하여 (x+1,y) 좌표에 다시 마스크 연산을 수행하고 그 결과를g(x+1,y)에 저장합니다. 이 과정을 영상 전체 픽셀에 대해 수행하면 필터링이 완료됩니다.
OpenCV는 영상의 필터링을 수행할 때, 영상의 가장자리 픽셀을 확장하여 영상 바깥쪽에 가상의 픽셀을 만듭니다. 이때 영상의 바깥쪽 가상의 픽셀 값을 어떻게 설정하는가에 따라 필터링 연산 결과가 달라집니다. OpenCV 필터링 연산에서 기본적으로 사용하는 가장자리 픽셀 확장 방법을 그림 7-3에 나타냈습니다.
엠보싱이란 직물이나 종이, 금속판 등에 올록볼록한 형태로 만든 객체의 윤곽 또는 무늬를 뜻하며, 엠보싱 필터는 입력 영상을 엠보싱 느낌이 나도록 변환하는 필터입니다.
7.2 블러링: 영상 부드럽게 하기
블러링(blurring)은 마치 초점이 맞지 않은 사진처럼 영상을 부드럽게 만드는 필터링 기법이며 스무딩(smoothing)이라고도 합니다.
7.2.1 평균값 필터
다양한 크기의 평균값 필터 마스크를 그림 7-6에 나타냈습니다. 그림 7-6에서 왼쪽 그림은 3×3 크기의 평균값 필터 마스크이고, 오른쪽 그림은 5×5 크기의 평균값 필터 마스크입니다.
7.2.2 가우시안 필터
평균값 필터보다 자연스러운 블러링 결과를 생성하는 가우시안 필터(Gaussian filter)에 대해 설명합니다. 가우시안 필터는 가우시안 분포(Gaussian distribution)함수를 근사하여 생성한 필터 마스크를 사용하는 필터링 기법입니다.
가우시안 분포는 평균과 표준 편차에 따라 분포 모양이 결정됩니다. 다만 영상의 가우시안 필터에서는 주로 평균이 0인 가우시안 분포 함수를 사용합니다. 평균이 0이고 표준 편차가σ인 1차원 가우시안 분포를 함수식으로 나타내면 다음과 같습니다.
가우시안 분포를 따르는 2차원 필터 마스크 행렬을 생성하려면 2차원 가우시안 분포 함수를 근사해야 합니다. 2차원 가우시안 분포 함수는x와y두 개의 변수를 사용하고, 분포의 모양을 결정하는 평균과 표준 편차도x축과y축 방향에 따라 따로 설정합니다. 평균이 (0, 0)이고x축과y축 방향의 표준 편차가 각각σx,σy인 2차원 가우시안 분포 함수는 다음과 같이 정의됩니다.
OpenCV에서 가우시안 필터링을 수행하려면GaussianBlur()함수를 사용합니다.GaussianBlur()함수 원형은 다음과 같습니다.
getGaussianKernel()함수는 표준 편차가sigma인 1차원 가우시안 분포 함수로부터ksize×1 크기의 필터 마스크 행렬을 생성하여 반환합니다.ksize는 (8sigma+ 1)보다 같거나 크게 지정하는 것이 좋습니다. 이 행렬의 원소에 저장되는 값은 다음 수식을 따릅니다.
7.3 샤프닝: 영상 날카롭게 하기 샤프닝은 초점이 잘 맞은 사진처럼 사물의 윤곽이 뚜렷하고 선명한 느낌이 나도록 영상을 변경하는 필터링 기법입니다.
7.3.1 언샤프 마스크 필터 블러링이 적용되어 부드러워진 영상을 활용하여 반대로 날카로운 영상을 생성한다는 것이죠. 여기서 블러링이 적용된 영상, 즉 날카롭지 않은 영상을 언샤프(unsharp)하다고 말하기도 합니다. 이처럼 언샤프한 영상을 이용하여 역으로 날카로운 영상을 생성하는 필터를 언샤프 마스크 필터(unsharp mask filter)라고 합니다.
7.4 잡음 제거 필터링
7.4.1 영상과 잡음 모델 신호 처리 관점에서 잡음(noise)이란 원본 신호에 추가된 원치 않은 신호를 의미합니다. 영상에서 잡음은 주로 영상을 획득하는 과정에서 발생하며, 디지털 카메라에서 사진을 촬영하는 경우에는 광학적 신호를 전기적 신호로 변환하는 센서(sensor)에서 주로 잡음이 추가됩니다.
디지털 카메라에서 카메라 렌즈가 바라보는 장면을 원본 신호 s(x, y)라고 하고, 여기에 추가되는 잡음을 n(x, y)라고 표현한다면 실제로 카메라에서 획득되는 영상 신호 f(x, y)는 보통 다음과 같이 표현합니다.
7.4.2 양방향 필터
1998년 토마시(C. Tomasi)가 제안한 양방향 필터(bilateral filter)는 에지 성분은 그대로 유지하면서 가우시안 잡음을 효과적으로 제거하는 알고리즘입니다[Tomasi98]. 양방향 필터 기능은 OpenCV 라이브러리 초기 버전부터 포함되어 있어서 많은 사람들이 사용하고 있습니다.
양방향 필터는 다음 공식을 사용하여 필터링을 수행합니다.
앞 수식에서f는 입력 영상,g는 출력 영상, 그리고p와q는 픽셀의 좌표를 나타냅니다.fp와fq는 각각p점과q점에서의 입력 영상 픽셀 값이고,gp는p점에서의 출력 영상 픽셀 값입니다.Gσs와Gσr는 각각 표준 편차가σs와σr인 가우시안 분포 함수입니다.S는 필터 크기를 나타내고,Wp는 양방향 필터 마스크 합이 1이 되도록 만드는 정규화 상수입니다. 양방향 필터 수식은 매우 복잡해 보이지만 가만히 살펴보면 두 개의 가우시안 함수 곱으로 구성된 필터입니다.
7.4.3 미디언 필터
미디언 필터(median filter)는 입력 영상에서 자기 자신 픽셀과 주변 픽셀 값 중에서 중간값(median)을 선택하여 결과 영상 픽셀 값으로 설정하는 필터링 기법입니다.
영상은 일종의 2차원 행렬이기 때문에 행렬의 산술 연산(arithmetic operation)을 그대로 적용할 수 있습니다. 즉, 두 개의 영상을 서로 더하거나 빼는 연산을 수행함으로써 새로운 결과 영상을 생성할 수 있습니다.
OpenCV에서 두 영상의 가중치 합을 구하려면 addWeighted() 함수를 사용합니다. addWeighted() 함수 원형은 다음과 같습니다.
add() 함수를 사용하여 두 개의 영상을 더하는 코드는 다음과 같이 작성할 수 있습니다.
Mat src1 = imread(“aero2.bmp”, IMREAD_GRAYSCALE); Mat src2 = imread(“camera.bmp”, IMREAD_GRAYSCALE);
Mat dst; add(src1, src2, dst);
만약 덧셈 연산의 두 입력 영상 타입이 같다면 add() 함수 대신 + 연산자 재정의를 사용할 수 있습니다. 다음은 + 연산자 재정의를 이용하여 두 영상을 더하는 예제 코드입니다.
Mat src1 = imread(“aero2.bmp”, IMREAD_GRAYSCALE); Mat src2 = imread(“camera.bmp”, IMREAD_GRAYSCALE);
Mat dst = src1 + src2;
두 영상을 더할 때 각 영상에 가중치를 부여하여 덧셈 연산을 할 수도 있습니다. 두 개의 행렬에 각각 가중치를 부여하여 덧셈하는 연산을 수식으로 표현하면 다음과 같습니다.
Mat src1 = imread(“aero2.bmp”, IMREAD_GRAYSCALE); Mat src2 = imread(“camera.bmp”, IMREAD_GRAYSCALE);
Mat dst; addWeighted(src1, 0.5, src2, 0.5, 0, dst);
OpenCV에서는 subtract() 함수를 통해 두 영상의 뺄셈 연산을 수행할 수 있습니다. subtract() 함수의 인자 구성과 설명은 add() 함수와 동일합니다.
OpenCV에서는absdiff()함수를 이용하여 차영상을 구할 수 있습니다.
차이 연산을 이용하면 두 개의 영상에서 변화가 있는 영역을 쉽게 찾을 수 있습니다. 그림 6-4는 차이 연산을 이용하여 움직임이 있는 영역을 추출하는 예입니다.
영상도 일종의 행렬이므로 두 입력 영상을 행렬로 생각하여 행렬의 곱셈을 수행할 수도 있습니다. 그러나 영상을 이용하여 행렬의 곱셈을 수행하는 경우는 거의 없습니다. 다만 두 영상에서 같은 위치에 있는 픽셀 값끼리 서로 곱하거나 나누는 연산을 수행할 수 있으며, 이를 위해 OpenCV에서는 multiply() 함수와 divide() 함수를 제공합니다.
6.2 영상의 논리 연산
영상의 논리 연산(logical operation)은 픽셀 값을 이진수로 표현하여 각 비트(bit) 단위 논리 연산을 수행하는 것을 의미합니다. OpenCV에서는 다양한 논리 연산 중에서 논리곱(AND), 논리합(OR), 배타적 논리합(XOR), 부정(NOT) 연산을 지원합니다.
OpenCV에서는 영상의 비트 단위 논리 연산을 수행하는bitwise_and(),bitwise_or(),bitwise_xor(),bitwise_not()함수를 제공합니다. 각 함수의 이름에서 쉽게 알 수 있듯이,bitwise_and()함수는 비트 단위 논리곱,bitwise_or()함수는 비트 단위 논리합,bitwise_xor()함수는 비트 단위 배타적 논리합,bitwise_not()함수는 비트 단위 부정 연산을 수행합니다. 각 함수의 인자 구성은 다음과 같습니다.
OpenCV에서 영상 파일을 그레이스케일 형태로 불러오려면 imread() 함수의 두 번째 인자에 IMREAD_GRAYSCALE 플래그를 설정해야 합니다.
Mat img1 = imread(“lenna.bmp”, IMREAD_GRAYSCALE);
그레이스케일 영상을 저장할 새로운 Mat 객체를 생성하려면 CV_8UC1 타입의 객체를 생성해야 합니다.
Mat img2(480, 640, CV_8UC1, Scalar(0));
만약 이미 3채널 컬러 영상을 가지고 있고, 이 영상을 그레이스케일 영상으로 변환하려면 cvtColor() 함수를 사용합니다. 다음 코드는 3채널 컬러 영상을 1채널 그레이스케일 영상으로 변환하는 코드입니다.
Mat img3 = imread(“lenna.bmp”, IMREAD_COLOR); Mat img4; cvtColor(img3, img4, COLOR_BGR2GRAY);
5.1.2 영상의 밝기 조절
영상의 밝기(brightness) 조절이란 영상의 전체적인 밝기를 조절하여 좀 더 밝거나 어두운 영상을 만드는 작업
dst(x,y) = src(x,y) + n
원소 자료형이 가질 수 있는 값의 범위를 벗어나는 경우 해당 자료형의 최솟값 또는 최댓값으로 원소 값을 설정하는 연산을 OpenCV에서는 포화(saturate)연산이라고 부릅니다.uchar자료형을 사용하는 그레이스케일 영상에 대해 포화 연산을 수식으로 나타내면 다음과 같습니다.
5.1.3 영상의 밝기 조절 직접 구현하기
5.1.4 트랙바를 이용한 영상의 밝기 조절
5.2 영상의 명암비 조절
5.2.1 기본적인 명암비 조절 방법 명암비란 영상에서 밝은 영역과 어두운 영역 사이에 드러나는 밝기 차이의 강도를 의미하고, 명암 대비 또는 콘트라스트(contrast)라고도 합니다.
5.2.2 효과적인 명암비 조절 방법
명암비를 효과적으로 높이기 위해서는 밝은 픽셀은 더욱 밝게, 어두운 픽셀은 더욱 어두워지게 변경해야 합니다. 이때 픽셀 값이 밝고 어둡다는 기준을 어떻게 설정할 것인지가 명암비 조절 결과 영상의 품질 차이를 가져올 수 있습니다.
5.3 히스토그램 분석
영상의 히스토그램(histogram)이란 영상의 픽셀 값 분포를 그래프 형태로 표현한 것을 의미합니다. 그레이스케일 영상의 경우, 각 그레이스케일 값에 해당하는 픽셀의 개수를 구하고 이를 막대 그래프 형태로 표현함으로써 히스토그램을 구할 수 있습니다. 컬러 영상에 대해서도 세 개의 색상 성분 조합에 따른 픽셀 개수를 계산하여 히스토그램을 구할 수 있습니다.
OpenCV에서 영상의 히스토그램을 구하려면 calcHist() 함수를 사용합니다. calcHist() 함수는 한 장의 영상뿐만 아니라 여러 장의 영상으로부터 히스토그램을 구할 수 있고, 여러 채널로부터 히스토그램을 구할 수도 있습니다. 또한 히스토그램 빈 개수도 조절할 수 있습니다. 다양한 형식의 히스토그램 생성을 지원하기 때문에 calcHist() 함수의 사용법은 꽤 복잡한 편입니다. calcHist() 함수 원형과 인자에 대한 설명은 다음과 같습니다.
5.3.2 히스토그램 스트레칭
히스토그램 스트레칭(histogram stretching)은 영상의 히스토그램이 그레이스케일 전 구간에 걸쳐서 나타나도록 변경하는 선형 변환 기법입니다. 보통 명암비가 낮은 영상은 히스토그램이 특정 구간에 집중되어 나타나게 되는데, 이러한 히스토그램을 마치 고무줄을 잡아 늘이듯이 펼쳐서 히스토그램 그래프가 그레이스케일 전 구간에서 나타나도록 변환하는 기법입니다. 히스토그램 스트레칭을 수행한 영상은 명암비가 높아지기 때문에 대체로 보기 좋은 사진으로 바뀌게 됩니다.
5.3.3 히스토그램 평활화
히스토그램 평활화(histogram equalization)는 히스토그램 스트레칭과 더불어 영상의 픽셀 값 분포가 그레이스케일 전체 영역에서 골고루 나타나도록 변경하는 알고리즘의 하나입니다. 히스토그램 평활화는 히스토그램 그래프에서 특정 그레이스케일 값 근방에서 픽셀 분포가 너무 많이 뭉쳐 있는 경우 이를 넓게 펼쳐 주는 방식으로 픽셀 값 분포를 조절합니다. 히스토그램 평활화는 히스토그램 균등화 또는 히스토그램 평탄화라는 용어로도 번역되어 사용되고 있습니다.
히스토그램 누적 함수H(g)는 다음 수식으로 정의됩니다.
만약 입력 영상의 픽셀 개수를N이라고 표기하면 히스토그램 평활화는 다음과 같은 형태로 정의됩니다.
OpenCV에서는 카메라 또는 동영상 파일로부터 정지 영상 프레임을 받아 올 때VideoCapture클래스를 이용합니다.VideoCapture클래스의 정의와 다양한 멤버 함수 사용법에 대해 알아보고, 실제로 카메라와 동영상 파일을 재생하는 소스 코드 작성 방법에 대해 알아보겠습니다. 또한 여러 장의 정지 영상을 동영상 파일로 저장할 때 사용할 수 있는VideoWriter클래스에 대해서도 소개합니다.
4.1.1 VideoCapture 클래스
동영상이란 일련의 정지 영상을 압축하여 파일로 저장한 형태입니다. 이때 동영상에 저장되어 있는 일련의 정지 영상을 프레임(frame)이라고 합니다. OpenCV에서는 VideoCapture라는 하나의 클래스를 이용하여 카메라 또는 동영상 파일로부터 정지 영상 프레임을 받아 올 수 있습니다. 간략화한 VideoCapture 클래스 정의를 코드 4-1에 나타냈습니다. 참고로 VideoCapture 클래스의 멤버 변수는 모두 protected: 모드로 선언되어 있어서 사용자가 직접 접근할 수 없으며, 코드 4-1에는 표시를 생략했습니다.
먼저VideoCapture클래스를 사용하여 동영상 파일을 불러오는 기능에 대해 알아보겠습니다.VideoCapture클래스에서 동영상 파일을 불러오려면 처음VideoCapture객체를 생성할 때 생성자에 동영상 파일 이름을 지정하거나 또는 기본 생성자로VideoCapture객체를 생성한 후VideoCapture::open()멤버 함수를 호출해야 합니다. 이때 사용하는VideoCapture생성자와VideoCapture::open()멤버 함수 원형은 다음과 같습니다.
카메라 장치를 열 때에도VideoCapture생성자 혹은VideoCapture::open()멤버 함수를 사용하는데, 이때는 함수의 인자에 문자열이 아니라 정수 값을 전달합니다. 다음에 나타낸VideoCapture생성자와VideoCapture::open()멤버 함수는 카메라 장치를 열 때 사용합니다.
카메라 장치를 사용하려고 할 때VideoCapture클래스의 생성자 혹은VideoCapture::open()함수에 전달하는 정수 값index는 다음과 같은 형태로 구성됩니다.
index = camera_id + domain_offset_id
만약 컴퓨터에 한 대의 카메라만 연결되어 있다면 이 카메라의camera_id값은 0입니다. 두 대 이상의 카메라가 연결되어 있다면 각각의 카메라는 0보다 같거나 큰 정수를 ID로 갖습니다.
카메라 또는 동영상 파일 열기를 수행한 후에는VideoCapture::isOpened()멤버 함수를 이용하여 열기 작업이 성공적으로 수행되었는지 확인하는 것이 좋습니다.
카메라 장치 또는 동영상 파일의 사용이 끝나면VideoCapture::release()함수를 호출하여 사용하던 자원을 해제해야 합니다. 참고로VideoCapture클래스의 소멸자에도VideoCapture::release()함수와 마찬가지로 사용하고 있던 자원을 모두 해제하는 코드가 들어가 있어서VideoCapture객체가 소멸할 때 자동으로 열려 있던 카메라 장치 또는 동영상 파일이 닫히게 됩니다.VideoCapture::release()함수 원형은 다음과 같습니다.
이번에는 카메라 또는 동영상 파일로부터 한 프레임의 정지 영상을 받아 오는 방법에 대해 알아보겠습니다.VideoCapture클래스를 이용하여 카메라 또는 동영상 파일을 정상적으로 열었다면, 그 후에는 공통의 멤버 함수를 사용하여 프레임을 받아 올 수 있습니다.VideoCapture클래스에서 한 프레임을 받아 오기 위해서는VideoCapture::operator >>()연산자 재정의 함수 또는VideoCapture::read()함수를 사용합니다.
현재 열려 있는 카메라 장치 또는 동영상 파일로부터 여러 가지 정보를 받아 오기 위해서는VideoCapture::get()함수를 사용합니다.
예를 들어 시스템 기본 카메라를 열고, 카메라의 기본 프레임 크기를 확인하려면 다음과 같이 코드를 작성할 수 있습니다.
VideoCapture cap(0);
int w = cvRound(cap.get(CAP_PROP_FRAME_WIDTH)); int h = cvRound(cap.get(CAP_PROP_FRAME_HEIGHT));
VideoCapture::get()함수와 반대로 현재 열려 있는 카메라 또는 비디오 파일 재생과 관련된 속성 값을 설정할 때에는VideoCapture::set()함수를 사용합니다.
4.2 다양한 그리기 함수
4.2.1 직선 그리기
영상 위에 직선을 그리는 line() 함수입니다. line() 함수도 cv 네임스페이스에서 선언되어 있지만 이 책에서는 간단히 line() 함수라고 표기하겠습니다. line() 함수 원형은 다음과 같습니다.
만약 화살표 형태의 직선을 그려야 하는 경우에는arrowedLine()함수를 이용하면 편리합니다.arrowedLine()함수 원형은 다음과 같습니다.
OpenCV 함수 중에서drawMarker()함수는 직선 그리기 함수를 이용하여 다양한 모양의 마커(marker)를 그립니다.drawMarker()함수 원형은 다음과 같습니다.
4.2.2 도형 그리기
사각형을 그리는 OpenCV 함수 이름은 rectangle()입니다. 그림을 그릴 사각형 정보는 사각형의 대각 위치에 있는 두 꼭지점 좌표를 이용하거나 또는 Rect 클래스 타입의 객체를 이용하여 전달할 수 있습니다. OpenCV는 다음과 같이 두 가지 형식의 rectangle() 함수를 제공합니다.
OpenCV에서 원을 그리는 함수는circle()입니다. 원을 그리기 위해서는 원의 중심점 좌표와 반지름을 지정해야 합니다.circle()함수 원형은 다음과 같습니다.
OpenCV에서 타원을 그리고 싶을 때에는ellipse()함수를 사용할 수 있습니다. 타원을 그리는 방식은 원보다는 좀 더 복잡합니다.ellipse()함수 원형은 다음과 같습니다.
polylines()함수를 사용할 수 있습니다.polylines()함수에는 다각형의 꼭지점 좌표를 전달해야 하며, 꼭지점 좌표는 보통vector<Point>자료형에 저장하여 전달합니다.polylines()함수 원형은 다음과 같습니다.
4.2.3 문자열 출력하기
OpenCV는 영상 위에 정해진 폰트로 문자열을 출력하는 putText() 함수를 제공합니다. putText() 함수 원형은 다음과 같습니다.
4.3 이벤트 처리
OpenCV에서 유용하게 사용할 수 있는 키보드, 마우스, 트랙바 이벤트 처리 방법에 대해 알아보겠습니다.
4.3.1 키보드 이벤트 처리
HelloCV 프로그램에서 키 입력을 확인하기 위해 사용한 함수는 waitKey() 함수이고, 이 함수가 키보드 입력을 처리하는 기본적인 OpenCV 함수입니다. 다시 한 번 waitkey() 함수의 원형을 소개하면 다음과 같습니다.
4.3.2 마우스 이벤트 처리
OpenCV에서 특정 창에 마우스 콜백 함수를 등록할 때에는 setMouseCallback() 함수를 사용합니다. setMouseCallback() 함수의 원형과 사용법은 다음과 같습니다.
마우스 콜백 함수는 마우스 이벤트가 발생할 때 자동으로 호출되는 함수이며, 이 콜백 함수의 형식 MouseCallback은 다음과 같이 정의되어 있습니다.
typedef void (*MouseCallback)(int event, int x, int y, int flags, void* userdata);
4.3.3 트랙바 사용하기
OpenCV에서 트랙바를 생성하려면 createTrackbar() 함수를 사용하며, 이 함수의 원형은 다음과 같습니다.
4.4 OpenCV 데이터 파일 입출력
4.4.1 FileStorage 클래스 OpenCV에서 데이터 파일 입출력은 FileStorage 클래스가 담당합니다. FileStorage 클래스는 OpenCV에서 사용하는 데이터의 파일 입출력 기능을 캡슐화하여 지원하는 클래스입니다.
FileStorage 객체를 생성한 후에는 FileStorage::open() 함수를 이용하여 실제 사용할 파일을 열어야 합니다. FileStorage::open() 함수 원형은 다음과 같습니다.
4.4.2 데이터 파일 저장하기
FileStorage 클래스 객체에 데이터를 저장할 때 사용하는 << 연산자 재정의 함수 원형은 다음과 같습니다.
4.4.3 데이터 파일 불러오기
특정 이름으로 저장되어 있는 FileNode 객체에 접근하려면 FileStorage::operator[]() 연산자 재정의 함수를 사용합니다.
일단 노드 이름을 이용하여FileNode객체를 얻어 온 후에는FileNode클래스의>>연산자 재정의 함수를 이용하여 노드에 저장된 데이터 값을 받아 올 수 있습니다.FileNode객체와 함께 사용하는>>연산자 재정의 함수 원형은 다음과 같습니다.
4.5 유용한 OpenCV 기능
4.5.1 마스크 연산
Mat::setTo() 함수는 마스크 연산을 지원하는 함수이며, 이 함수의 원형을 다시 쓰면 다음과 같습니다.
마스크 연산을 지원하는Mat::copyTo()함수 원형은 다음과 같습니다.
4.5.2 연산 시간 측정
OpenCV에서는 getTickCount() 함수와 getTickFrequency() 함수를 사용하여 특정 연산의 수행 시간을 측정합니다.
4.5.3 유용한 OpenCV 함수 사용법
sum( ) 함수와 mean( ) 함수
주어진 행렬의 전체 원소 합 또는 평균을 구하는 일은 종종 필요합니다. OpenCV에서 Mat 행렬의 원소 합을 구하고 싶을 때에는 sum() 함수를 사용하고, 평균을 구하고 싶을 때에는 mean() 함수를 사용합니다. 이 두 함수는 4채널 이하의 행렬에 대해서만 동작하며, 합과 평균을 Scalar 타입으로 반환합니다. 두 함수의 원형은 다음과 같습니다.
minMaxLoc( ) 함수
다음으로 살펴볼 함수는 주어진 행렬의 최솟값, 최댓값을 찾는 minMaxLoc() 함수입니다. 이 함수는 최솟값, 최댓값이 있는 좌표 정보도 함께 알아낼 수 있습니다. minMaxLoc() 함수 원형은 다음과 같습니다.
normalize( ) 함수
다음으로는 행렬의 노름(norm) 값을 정규화하거나 또는 원소 값 범위를 특정 범위로 정규화할 때 사용할 수 있는 normalize() 함수에 대해 알아보겠습니다. normalize() 함수 원형은 다음과 같습니다.
cvRound( ) 함수
영상 처리를 수행하다 보면 내부 연산은 실수를 사용하고, 최종적인 결과는 정수로 변환하는 경우가 종종 있습니다. 실수 값을 정수로 변환할 때는 주로 반올림을 사용하며, OpenCV에서는 실수 값의 반올림 연산을 위해 cvRound() 함수를 제공합니다. cvRound() 함수 원형은 다음과 같습니다.
OpenCV에서 제공하는 여러 자료형 클래스 중에서 먼저 Point 클래스에 대해 알아보겠습니다. Point 클래스는 2차원 평면 위에 있는 점의 좌표를 표현하는 템플릿 클래스입니다.1 Point 클래스는 2차원 좌표를 나타내는 x와 y라는 이름의 멤버 변수를 가지고 있습니다.
다양한 자료형에 대한 Point 클래스 이름 재정의입니다. typedef Point<int> Point2i; typedef Point<int64> Point2l; typedef Point<float> Point2f; typedef Point_<double> Point2d; typedef Point2i Point;
Point_ 클래스는 템플릿 클래스이기 때문에 실제로 사용할 때에는 어떤 자료형으로 좌표를 표현할 것인지를 명시해야 합니다
Point_ 클래스를 사용하는 코드 작성 방법에 대해 알아보겠습니다. 2차원 정수 좌표계에서 점의 좌표를 표현하려면 다음과 같이 코드를 작성할 수 있습니다.
Point pt1; // pt1 = (0, 0) pt1.x = 5; pt1.y = 10; // pt1 = (5, 10) Point pt2(10, 30); // pt2 = (10, 30) 이 코드에서 변수 pt1은 기본 생성자를 사용하여 생성되었으며, 이 경우 멤버 변수 pt1.x와 pt1.y는 0으로 초기화됩니다. 그러므로 기본 생성자로 생성된 변수 pt1은 (0, 0) 좌표를 나타냅니다. 앞 코드의 두 번째 행에서는 pt1.x에 5를 대입하고, pt1.y에는 10을 대입함으로써 pt1 변수가 (5, 10) 좌표를 나타내도록 변경했습니다. 변수 pt2는 생성과 동시에 (10, 30) 좌표를 나타냅니다.
3.1.2 Size_ 클래스
영상 또는 사각형 영역의 크기를 표현할 때에는 Size 클래스를 사용합니다. Size 클래스는 사각형 영역의 가로와 세로 크기를 나타내는 width와 height 멤버 변수를 가지고 있습니다.
다양한 자료형에 대한 Size 클래스 이름 재정의입니다. typedef Size<int> Size2i; typedef Size<int64> Size2l; typedef Size<float> Size2f; typedef Size_<double> Size2d; typedef Size2i Size;
Point 클래스와 마찬가지로 Size 클래스도 템플릿으로 정의되어 있으며, 다양한 자료형에 대해 이름이 재정의되어 있습니다.
Size 클래스를 사용하는 코드 작성 방법에 대해 알아보겠습니다. 2차원 정수 좌표계에서 크기를 표현하려면 다음과 같이 코드를 작성할 수 있습니다.
Size sz1, sz2(10, 20); // sz1 = [0 x 0], sz2 = [10 x 20] sz1.width = 5; sz1.height = 10; // sz1 = [5 x 10] 이 코드에서 오른쪽 주석으로 표시된 부분은 연산에 의해 생성되는 Size 객체의 크기를 나타냅니다. 변수 sz1은 기본 생성자를 사용하여 생성하였으며, 이 경우 멤버 변수 sz1.width와 sz1.height는 0으로 초기화됩니다. 그러므로 기본 생성자로 생성된 변수 sz1은 0×0의 크기를 나타내고, 이는 유효하지 않은 크기 객체입니다. 변수 sz2는 생성과 동시에 10×20의 크기를 나타냅니다. 앞 코드의 두 번째 행에서는 sz1.width에 5를 대입하고, sz1.height에는 10을 대입함으로써 5×10의 크기를 나타내도록 변경했습니다.
3.1.3 Rect_ 클래스
OpenCV에서 사각형의 위치와 크기 정보를 표현할 때에는 Rect 클래스를 사용합니다. Rect 클래스는 사각형의 좌측 상단 점의 좌표를 나타내는 x, y 멤버 변수와 사각형의 가로 및 세로 크기를 나타내는 width, height 멤버 변수를 가지고 있습니다.
다양한 자료형에 대하여 Rect_ 클래스 이름 재정의입니다. typedef Rect<int> Rect2i; typedef Rect<float> Rect2f; typedef Rect<double> Rect2d; typedef Rect2i Rect;
Rect_ 클래스도 템플릿으로 정의되어 있으며, 다양한 자료형에 대해 이름이 재정의되어 있습니다. int 자료형을 이용하여 사각형 정보를 표현하려면 Rect2i 클래스를 사용하고, float 자료형으로 사각형을 표현하려면 Rect2f 클래스를 사용합니다. 특히 정수형으로 사각형 정보를 표현하는 경우가 많기 때문에 Rect2i는 다시 Rect라는 이름으로 재정의되어 있습니다. 즉, Rect 클래스는 정수형 멤버 변수 x, y, width, height를 가지고 있는 사각형 표현 클래스입니다.
3.1.4 RotatedRect 클래스
RotatedRect 클래스는 회전된 사각형을 표현하는 클래스입니다. RotatedRect 클래스는 회전된 사각형의 중심 좌표를 나타내는 center, 사각형의 가로 및 세로 크기를 나타내는 size, 회전 각도 정보를 나타내는 angle을 멤버 변수로 가집니다.
RotatedRect클래스를 사용하는 코드 작성 방법에 대해 알아보겠습니다. 중심 좌표가 (40, 30), 크기는 40×20, 시계 방향으로 30°만큼 회전된 사각형 객체는 다음 코드를 이용하여 생성할 수 있습니다.
만약 회전된 사각형 객체의 네 꼭지점 좌표를 알고 싶다면RotatedRect::points()멤버 함수를 사용합니다.RotatedRect::points()함수에는 크기가 4인Point2f자료형의 배열 이름을 전달합니다.Point2f pts[4]; rr1.points(pts);
이 코드를 실행하면 회전된 사각형의 네 꼭지점 좌표가pts배열에 저장됩니다. 실제로 코드를 실행하면pts[0] = (17.6795,28.6603),pts[1] = (27.6795,11.3397),pts[2] = (62.3205,31.3397),pts[3] = (52.3205,48.6603)형태로 좌표가 설정됩니다.RotatedRect::points()함수는 사각형의 좌측 하단 꼭지점부터 시계 방향으로 꼭지점 좌표를 추출합니다.
경우에 따라서는 회전된 사각형을 감싸는 최소 크기의 사각형 정보가 필요합니다. 이처럼 특정 객체를 감싸는 최소 크기의 사각형을 바운딩 박스(bounding box)라고 합니다. 회전된 사각형의 바운딩 박스를 구하려면RotatedRect::boundingRect()멤버 함수를 사용합니다.Rect br = rr1.boundingRect();
3.1.5 Range 클래스
Range클래스는 범위 또는 구간을 표현하는 클래스입니다.Range클래스는 범위의 시작과 끝을 나타내는start와end멤버 변수를 가지고 있습니다.
3.1.6 String 클래스
OpenCV에서는 영상 출력 창에 고유의 문자열을 지정하여 구분하고, 영상에 문자열을 출력하는 기능도 제공합니다. C++ 표준 라이브러리(STL)에서std::string클래스를 이용하여 문자열을 저장하고 처리하듯이 OpenCV에서는cv::String클래스를 사용하여 문자열을 저장하고 처리할 수 있습니다.
3.2 Mat 클래스
OpenCV에서 가장 많이 사용하는 클래스는 행렬을 나타내는Mat클래스입니다. 그러므로 OpenCV 라이브러리를 잘 다루기 위해서는Mat클래스를 제대로 이해하고 있어야 합니다.
3.2.1 Mat 클래스 개요
OpenCV 라이브러리에서 가장 많이 사용하는 클래스는 단연 행렬을 표현하는Mat클래스입니다.Mat클래스는 일반적인 2차원 행렬뿐만 아니라 고차원 행렬을 표현할 수 있으며, 한 개 이상의 채널(channel)을 가질 수 있습니다.Mat클래스에는 정수, 실수, 복소수 등으로 구성된 행렬 또는 벡터(vector)를 저장할 수 있고, 그레이스케일 또는 컬러 영상을 저장할 수도 있습니다.
Mat클래스는 <opencv-src>\modules\core\include\opencv2\core\mat.hpp 파일에 정의되어 있습니다. 여기서 <opencv-src>는 OpenCV 소스 코드가 있는 폴더 위치를 의미합니다.
#define CV_8U 0 // uchar, unsigned char #define CV_8S 1 // schar, signed char #define CV_16U 2 // ushort, unsigned short #define CV_16S 3 // signed short #define CV_32S 4 // int #define CV_32F 5 // float #define CV_64F 6 // double #define CV_16F 7 // float16_t
3.2.2 행렬의 생성과 초기화
Mat객체를 생성함과 동시에 원소 값 저장을 위한 메모리 공간을 할당하려면 다음 생성자를 사용합니다.
Mat img2(480, 640, CV_8UC1); // unsigned char, 1-channel
Mat img3(480, 640, CV_8UC3); // unsigned char, 3-channels
Mat클래스 생성자에서 행렬의 크기를 지정할 때Size클래스를 사용할 수도 있습니다.
Mat img4(Size(640, 480), CV_8UC3); // Size(width, height)
정해진 크기와 타입의Mat객체를 생성하고 모든 원소 값을 초기화하려면 다음 형태의 생성자를 사용합니다.
Mat img5(480, 640, CV_8UC1, Scalar(128)); // initial values, 128 Mat img6(480, 640, CV_8UC3, Scalar(0, 0, 255)); // initial values, red
OpenCV에서 모든 원소가 0으로 초기화된 행렬을 만드는 함수 이름은Mat::zeros()입니다.
Mat mat1 = Mat::zeros(3, 3, CV_32SC1); // 0‘s matrix
행렬의 모든 원소가 1로 초기화된 행렬을 생성하려면Mat::ones()함수를 사용할 수 있습니다. 또한 행렬 연산에서 자주 사용되는 단위 행렬(identity matrix)을 생성하려면Mat::eye()함수를 사용할 수 있습니다.Mat::ones()와Mat::eye()함수 원형은 다음과 같습니다.
Mat mat2 = Mat::ones(3, 3, CV_32FC1); // 1‘s matrix
Mat mat3 = Mat::eye(3, 3, CV_32FC1); // identity matrix
3.2.3 행렬의 복사
Mat img2 = img1; // 복사생성자(얕은복사)
Mat img3; img3 = img1; // 대입연산자(얕은복사)
만약 복사본 영상을 새로 생성할 때, 픽셀 데이터를 공유하는 것이 아니라 메모리 공간을 새로 할당하여 픽셀 데이터 전체를 복사하고 싶다면Mat::clone()또는Mat::copyTo()함수를 사용해야 합니다.
Mat img4 = img1.clone(); // 깊은복사
Mat img5; img1.copyTo(img5); // 깊은복사
3.2.4 부분 행렬 추출
Mat클래스로 정의된 행렬에서 특정 사각형 영역의 부분 행렬을 추출하고 싶을 때에는Mat클래스에 정의된 괄호 연산자 재정의를 사용합니다.
Mat img1 = imread(“cat.bmp”);
Mat img2 = img1(Rect(220, 120, 340, 240)); // 얕은복사
영상의 반전은Mat클래스 타입의 변수 앞에~연산자를 붙이는 방식으로 쉽게 적용할 수 있습니다.
img2 = ~img2;
만약 독립된 메모리 영역을 확보하여 부분 영상을 추출하고자 한다면 괄호 연산자 뒤에Mat::clone()함수를 함께 사용해야 합니다. 즉,Mat클래스의 괄호 연산자 재정의 함수를 사용한 후, 바로 뒤에.clone()코드를 붙여서 사용하면 독립된 복사본의 부분 영상을 만들 수 있습니다. 다음은 실제 코드 사용 예입니다.
Mat img3 = img1(Rect(220, 120, 340, 240)).clone();
Mat행렬에서 특정 범위의 행 또는 열을 부분 행렬로 추출하고자 할 때에는Mat::rowRange()또는Mat::colRange()함수를 사용할 수 있습니다.Mat::rowRange()함수는 지정한 범위의 행으로 구성된 행렬을 반환하고,Mat::colRange()함수는 지정한 범위의 열로 구성된 행렬을 반환합니다. 행 또는 열의 범위는 두 개의 int 값으로 지정할 수도 있고, 또는Range클래스 객체를 이용하여 지정할 수 있습니다.
3.2.5 행렬의 원소 값 참조
OpenCV에서 제공하는 기능 외에 사용자가 직접 자신만의 알고리즘을 구현하여 적용해야 하는 경우도 자주 발생합니다. 이때 필요한 기능이 영상의 픽셀 값을 참조하는 기능입니다. OpenCV는Mat클래스에 저장된 행렬 원소 값을 참조하고 값을 변경할 수 있는 다양한 인터페이스를 제공합니다. 이 절에서는 OpenCV에서 제공하는 세 가지 픽셀 값 접근 방법에 대해 알아보고, 각 방법의 장단점에 대해 살펴보겠습니다.
Mat::at( ) 함수 사용 방법
OpenCV에서 제공하는 가장 직관적인 행렬 원소 접근 방법은Mat::at()멤버 함수를 사용하는 방법입니다.Mat::at()함수는 보통 행과 열을 나타내는 두 개의 정수를 인자로 받아 해당 위치의 행렬 원소 값을 참조 형식으로 반환합니다.Mat::at()함수는 템플릿을 사용하는 템플릿 함수로서 여러 가지 형태로 재정의가 되어 있으며, 주로 사용하는Mat::at()함수 형식은 다음과 같습니다.
Mat::at()함수는 템플릿 함수로 정의되어 있기 때문에Mat::at()함수를 사용할 때에는 행렬 원소 자료형을 명시적으로 지정해야 합니다.
for (int j = 0; j < mat1.rows; j++) { for (int i = 0; i < mat1.cols; i++) { mat1.at<uchar>(j, i)++; } }
Mat::ptr( ) 함수 사용 방법
두 번째로 살펴볼 행렬 원소 접근 방법은Mat::ptr()멤버 함수를 이용하는 방법입니다.Mat::ptr()함수는Mat행렬에서 특정 행의 첫 번째 원소 주소를 반환합니다.Mat::ptr()함수는 여러 가지 형식으로 재정의되어 있지만, 가장 널리 사용하는Mat::ptr()함수 형식은 다음과 같습니다.
for (int j = 0; j < mat1.rows; j++) { uchar p = mat1.ptr<uchar>(j); for (int i = 0; i < mat1.cols; i++) { p[i]++; } }
MatIterator_ 반복자 사용 방법
앞서 설명한Mat::at()또는Mat::ptr()함수를 사용하여 행렬의 원소를 참조할 경우, 함수 인자로 전달된 값이 행렬의 크기를 벗어나면 에러가 발생합니다. 그러므로 프로그램 코드를 작성할 때 사용자가 행렬 또는 영상의 크기를 충분히 고려해야 하며, 주의하지 않으면 예기치 않게 프로그램이 종료될 수 있습니다. 이러한 단점을 해소하기 위해 OpenCV는 반복자(iterator)개념을 도입하여 행렬 원소를 참조할 수 있는 방법을 제공합니다. 즉,Mat행렬 원소 참조를 위한 반복자 변수를 만들어서 행렬 크기에 상관없이 행렬 전체 원소를 차례대로 참조하는 방식입니다.
for (MatIterator_<uchar> it = mat1.begin<uchar>(); it != mat1.end<uchar>(); ++it) { (*it)++; }
3.2.6 행렬 정보 참조하기
3.2.7 행렬 연산
표 3-3에 나열된 행렬 연산 예제 중에서*연산자를 사용하는 곱셈 연산은 행렬의 수학적 곱셈 연산을 의미합니다. 만약 두 행렬에서 같은 위치에 있는 원소끼리 곱셈 연산을 수행하려면Mat::mul()멤버 함수를 사용해야 합니다.Mat::mul()함수 원형은 다음과 같습니다.
행렬과 관련된 중요한 연산 중에 역행렬(inverse matrix)을 구하는 연산이 있습니다. OpenCV에서 행렬의 역행렬을 구할 때에는Mat::inv()멤버 함수를 사용합니다.Mat::inv()함수 원형은 다음과 같습니다.
행렬의 행과 열을 서로 교환해서 만드는 전치 행렬(transpose matrix)은Mat::t()멤버 함수를 이용하여 구할 수 있습니다.Mat::t()함수 원형은 다음과 같습니다.
3.2.8 크기 및 타입 변환 함수
행렬의 타입을 변경할 때에는Mat::convertTo()함수를 사용합니다.Mat::convertTo()함수 원형은 다음과 같습니다.
Mat::reshape()함수는 주어진 행렬의 크기 또는 채널 수를 변경합니다.Mat::reshape()멤버 함수는 다양한 형식으로 재정의되어 있으며, 그중 널리 사용되는 형식은 다음과 같습니다.
다음은 Mat::reshape() 함수를 이용하여 3×4 크기의 행렬을 1×12 크기의 행렬로 변환하는 예제 코드입니다.
Mat::reshape()멤버 함수처럼 행렬의 모양을 변경시키는 것이 아니라 단순히 행렬의 행 크기를 변경하고 싶을 때에는Mat::resize()함수를 사용할 수 있습니다.
이미 존재하는 행렬에 원소 데이터를 추가하고 싶을 때에는Mat::push_back()멤버 함수를 사용할 수 있습니다.Mat::push_back()함수는 다음과 같이 다양한 형식으로 정의되어 있습니다.
3.3 Vec과 Scalar 클래스
3.3.1 Vec 클래스
하나의 행으로만 이루어진 행렬은 행 벡터라고 부르고, 하나의 열로만 구성된 행렬은 열 벡터라고 부릅니다. 그리고 행 벡터와 열 벡터를 합쳐서 벡터 또는 벡터 행렬이라고 부릅니다. 즉, 벡터는 같은 자료형을 가진 원소 몇 개로 구성된 데이터 형식이라고 볼 수 있습니다.
코드 3-14에서Matx클래스는 작은 크기의 행렬을 표현하기 위해 만들어진 템플릿 클래스입니다. 이 행렬 클래스는 원소 데이터를val이라는 이름의 배열에 저장합니다. 코드 3-14의 1행에서_Tp는 행렬 원소 자료형이고,m과n은 각각 행과 열 개수를 의미합니다.Vec클래스는Matx클래스를 상속받아 만들어지며, 열 개수가 1개로 특화된 벡터 표현 클래스입니다.Vec클래스도 템플릿을 사용하기 때문에 실제 코드에서 사용할 때에는<>괄호 사이에 데이터 자료형과 데이터 개수를 명시해야 합니다.
예를 들어 컬러 영상의 픽셀 값을 표현하고 싶을 때는Vec<uchar, 3>형식 대신에Vec3b클래스를 사용할 수 있습니다.Vec3b클래스를 사용하면 앞에서 설명한p1,p2변수 선언을 다음과 같이 작성할 수 있습니다.
그런데 Vec 클래스는 다음과 같이 [] 연산자 재정의가 되어 있기 때문에 [] 연산자를 이용하여 멤버 변수 val 배열에 쉽게 접근할 수 있습니다.
template<typename _Tp, int cn> inline _Tp& Vec<_Tp, cn>::operator [](int i) { CV_DbgAssert( (unsigned)i < (unsigned)cn ); return this->val[i]; } 이 코드에서 CV_DbgAssert() 매크로 함수는 디버그 모드에서만 동작하는 예외 처리 코드이며, 동작 성능을 고려하여 릴리스 모드에서는 무시됩니다. [] 연산자 재정의를 이용하여 p1 변수의 첫 번째 원소를 100으로 변경하려면 다음과 같이 코드를 작성합니다.
p1[0] = 100;
3.3.2 Scalar 클래스
OpenCV 프로그래밍에서Mat클래스 다음으로 자주 사용되는 클래스는Scalar클래스입니다.Scalar클래스는 4채널 이하의 영상에서 픽셀 값을 표현하는 용도로 자주 사용됩니다. 사실Scalar클래스는Scalar라는 이름의 클래스 템플릿 이름 재정의이며,Scalar클래스는Vec클래스를 상속받아 만들어졌습니다. 코드 3-15는 간략화한Scalar_클래스와Scalar이름 재정의를 보여 줍니다.
Scalar 클래스는 보통 네 개 이하의 채널을 갖는 영상의 픽셀 값을 표현하는 용도로 사용됩니다. 그레이스케일 영상의 경우, Scalar 클래스의 첫 번째 원소가 픽셀 밝기를 표현하고 나머지 세 개의 원소는 0으로 설정됩니다. 트루컬러 영상의 경우, Scalar 클래스의 처음 세 개 원소가 B(파란색), G(녹색), R(빨간색) 색상 성분 값을 표현하고, 네 번째 원소는 보통 0으로 설정됩니다. 간혹 PNG 파일 형식처럼 투명도를 표현하는 알파 채널이 있는 경우 Scalar 클래스의 네 번째 원소를 이용하기도 합니다. 자주 사용되는 Scalar 클래스 객체 생성 방법을 정리하면 다음과 같습니다.
InputArray클래스는 주로 OpenCV 함수의 입력으로 사용되고,OutputArray클래스는 OpenCV 함수의 출력으로 사용되는 인터페이스 클래스입니다.
3.4.1 InputArray 클래스
OpenCV 문서 사이트를 보면InputArray타입의 인자를 사용하는 함수를 자주 볼 수 있습니다. 영상의 화면 출력 함수인imshow()함수도 영상을InputArray타입으로 전달하도록 선언되어 있습니다.InputArray클래스는Mat,vector<T>등 다양한 타입을 표현할 수 있는 인터페이스 클래스로서 주로 OpenCV 함수의 입력 인자 자료형으로 사용됩니다.
InputArray클래스는 다음과 같은 형태로 이름 재정의되어 있습니다.
typedefconst_InputArray& InputArray;
만약 OpenCV에서 제공하는 함수처럼 사용자 정의 함수에서Mat객체뿐만 아니라vector<T>타입의 객체를 한꺼번에 전달받을 수 있게 만들고 싶다면 사용자 정의 함수 인자에InputArray타입을 사용할 수 있습니다. 그리고 실제 함수 본문에서는_InputArray클래스의 멤버 함수인_InputArray::getMat()함수를 사용하여Mat객체 타입 형태로 변환해서 사용해야 합니다.
코드 3-17은InputArray클래스 타입의 인자를 사용하는 함수를 정의하는 방법과 실제 사용 방법을 보여 주는 예제 코드입니다. 코드 3-17에 나온InputArrayOp()함수와printMat()함수를 사용하는 전체 코드는 내려받은 예제 파일에서 ch03/InputArrayOp 프로젝트에서 확인할 수 있습니다.
3.4.2 OutputArray 클래스
많은 OpenCV 함수는 영상을 입력으로 받아 영상 처리를 수행하고, 그 결과를 다시 영상으로 생성하여 반환합니다. 이때 출력 영상을 함수의return구문으로 반환하는 것이 아니라 보통OutputArray클래스의 참조를 함수 인자로 사용하여 결과 영상을 전달합니다.OutputArray클래스는 다음과 같이 이름 재정의가 되어 있습니다.
컴퓨터 비전 : 사람의 뇌가 하는 작업을 수학적 알고리즘을 통해 컴퓨터가 유사하게 수행할 수 있도록 만드는 작업
컴퓨터 비전 관련 분야
필요지식 :
- 고등학교 이상의 수학적 지식
- 특히 행렬과 미분에 대한 개념은 필수
-OpenCV 라이브러리가 C++ 언어로 구현되었기 때문에 C++ 문법에 대한 충분한 이해도 필요
- 클래스와 상속 관계, STL, 그리고 최신 C++ 문법인 C++11/14/17에 대한 지식도 어느 정도 필요
1.2 영상의 구조와 표현 방법
1.2.1 영상의 획득과 표현 방법
디지털 카메라에서 영상 획득 과정
밑에 그림에 표시한 영상은 가로 크기가 w이고, 세로 크기가 h인 영상입니다. 이 영상의 픽셀 좌표를 (x, y)로 표현할 경우, x는 0부터 w−1 사이의 정수를 가질 수 있고, y는 0부터 h−1 사이의 정수를 가질 수 있습니다. 이처럼 좌표의 시작을 0부터 표현하는 방식을 0-기반(zero-based) 표현이라고 부르며, 보통 컴퓨터에서 많이 사용하는 방식입니다.
디지털 영상 표현과 좌표계
1.2.2 그레이스케일 영상과 컬러 영상
그레이스케일 영상은 밝기 정보를 256단계로 구분하여 표현합니다. 즉, 그레이스케일 영상에서 하나의 픽셀은 0부터 255 사이의 정수 값을 가질 수 있으며, 0은 가장 어두운 검은색을 표현하고 255는 가장 밝은 흰색을 표현합니다. 그레이스케일 값을 저장하기 위하여 C/C++에서는 보통 unsigned char 자료형을 사용합니다. unsigned char 자료형은 1바이트(byte)의 크기를 가지며 부호 없는 8비트(bit) 정수 값을 저장할 수 있습니다.
트루컬러 영상은 보통 R, G, B 세 개의 색상 성분 조합으로 픽셀 값을 표현합니다. 여기서 R은 빨간색(red), G는 녹색(green), B는 파란색(blue)을 나타냅니다. 각각의 색상 성분은 0부터 255 사이의 정수 값으로 표현되며, 0은 해당 색상 성분이 전혀 없음을 의미하고 255는 해당 색상 성분이 가득 차 있음을 의미합니다. 트루컬러 영상에서 하나의 픽셀은 unsigned char 자료형 세 개를 이용하여 표현할 수 있습니다.
2장 OpenCV 설치와 기초 사용법
2.1 OpenCV 개요와 설치
2.1.1 OpenCV 개요
OpenCV의 공식 웹 사이트 주소는https://opencv.org/이고,이곳에서 OpenCV 라이브러리 설치 파일 및 소스 파일을 내려받을 수 있습니다.
- imwrite() : img 변수에 저장되어 있는 영상 데이터를 filename 이름의 파일로 저장
- namedWindow() : 두 개의 인자로 구성되어 있지만, 두 번째 인자는 기본 인자가 있으므로 winname 문자열 하나만 지정하여 사용할 수 있습니다. 원래 Windows 운영 체제에서는 각각의 창을 구분하기 위해 핸들(handle)이라는 숫자 값을 사용하지만, OpenCV에서는 각각의 창에 고유한 문자열을 부여하여 각각의 창을 구분합니다.
- imshow() : winname 창에 mat 인자로 전달된 영상 데이터를 출력
- waitKey() : delay에 해당하는 밀리초 시간 동안 키 입력을 기다립니다.
※그림 출처는 "OpenCV 4로 배우는 컴퓨터 비전과 머신 러닝" 책에 있는 것을 사용 하였습니다.