- 목표
openCV에서 사용하는 기본적인 Matrix 연산자(operator)에 대해 알아보기
- Color space conversion
색공간(Color space)이란?
Ex. RGB, HSV, YCbCr
색공간에 대한 자세한 개념은 밑의 게시글을 참고.
void cvtColor(Mat src, Mat dst, int code, int dstCn =0)
각각의 색공간 간의 변환을 시켜주는 함수
- Mat src : 입력 matrix
- Mat dst : 결과 matrix
- int code : 어떠한 색공간 사용할지 (Ex. CV_BGR2GRAY, CV_BGR2HSV, CV_BGR2YCrCb, CV_BGR2Lab …)
- int dstCn = 0 : 결과 matrix의 채널 설정. defalut 0은 source (입력=결과)
void split(Mat src, Mat* mv)
입력 mat가 여러채널을 가진 mat인 경우, 각각의 채널로 분리시켜주는 함수
merge(InputArrayOfArray mv, OutputArray dst): reverse of split
여러개의 영상(입력)을 하나의 영상으로 만들어줌(출력)
하나의 채널 영상들을 합쳐 여러 채널영상으로 만드는..
int main() {
Mat image, image_YUV, dst;
vector<Mat> yuv_channels(3); // mat크기 3으로 설정(split함수에 사용하기 위해)
image = imread("lena.png");
cvtColor(image, image_YUV, COLOR_BGR2YUV);
split(image_YUV, yuv_channels);
merge(yuv_channels, dst);
imshow("input image", image);
imshow("Y", yuv_channels[0]); // split이 이루어진 첫번째 값
imshow("U", yuv_channels[1]);
imshow("V", yuv_channels[2]);
imshow("YUV image", dst);
waitKey(0);
return 0;
}
input, output(merge) 결과가 다른이유 : openCV에서는 display하는 영상의 색공간이 rgb라고 가정하는데, merge할때 yuv값을 그냥 display시켰기 때문이다.
ROI(Region of Interest): 입력 영상 중에 특정한 부분(관심있어하는 부분)을 설정하는 것.
왜 설정하는가? 요새 영상은 해상도가 크기 때문에 복잡한 영상을 수행하기에 계산량이 많이 드는 단점이 있다. 검출하고 싶어하는 물체나 기타 선검출, 에지 검출을 특정한 영역에서만 해도 된다면 입력영상이 아무리 크더라도 ROI를 작게 설정하면 계산량을 줄일 수 있기 때문.
※ ROI를 설정한 뒤, ROI영상 내에서 수행한다면 입력영상에도 영향을 미친다!
int main() {
Mat image = imread("lena.png");
Rect rect(100, 30, 250, 300); // openCV에서 제공하는 사각형에 대한 데이터구조 Rect
Mat rect_roi = image(rect); // ROI 설정
imshow("rectROI", rect_roi); // ROI 출력
waitKey(0);
}
사각형 외에도 다른 형태로 ROI를 설정할 수 있다.
int main() {
Mat image = imread("lena.png");
Mat poly_roi;
Mat poly_mask = Mat::zeros(image.size(), image.type());
Point poly[1][4];
poly[0][0] = Point(226, 100);
poly[0][1] = Point(286, 100);
poly[0][2] = Point(316, 300);
poly[0][3] = Point(196, 300);
const Point* ppt[1] = { poly[0] };
int npt[] = { 4 };
// function that draws polygon with given points
fillPoly(poly_mask, ppt, npt, 1, Scalar(255, 255, 255), 8);
image.copyTo(poly_roi, poly_mask);
imshow("polyROI", poly_roi);
waitKey(0);
}
- Addition/Subtraction operation
void add (Mat src1, Mat src2, Mat dst, Mat mask= noArray(), int dtype = -1)
src1 + src2 to dst
- Mat mask=noArray() : 특정한 ROI부분에서만 연산을 수행하고자 할때 사용하는 파라미터. mask matrix를 따로 설정해줘서 0이 아닌 부분에서만 연산 수행, 0인부분은 연산을 수행하지 않는다.
- int dtype = -1 : depth, 즉 결과영상의 intensive level을 설정. -1이면 입력영상과 결과영상의 intesive level을 동일하게 설정.
- dst(I) = saturate(src1(I)+src2(I) if mask(I) ! = 0
(dst를 src1, src2와 동일하게 0~255값으로 설정해야하는 경우,)
src1의 특정한 픽셀이 255, src2도 그런경우에 더했을 때 dst는 255를 초과하게된다.
이럴때, saturate 함수를 통해서 255의 값을 그냥 255로, 결과가 0이하의 값이 나오는 경우에도 0으로 설정된다.
어떻게든 결과가 0~255사이의 값이 나오도록 함
int main() {
Mat img1 = imread("lena.jpg");
Mat img2 = imread("lena.png");
Mat dst;
add(img1, img2, dst);
imshow("dst", dst);
waitKey(0);
}
void scaleAdd(Mat src1, double scale, Mat src2, Mat dst)
src1에다가 특정한 scale값을 곱해줘서 더함.
dst(I) = scale * src1(I) + src2(I)
void absdiff(Mat src1, Mat src2, Mat dst)
src1에서 src2를 뺌. 이때, 결과는 절대값(음수 나올 수 없음)
dst(I) = saturate( | src1(I)-src2(I) | )
void subtract(Mat src1, Mat src2, Mat dst, Mat mask=noArray(), int dtype = -1)
src1에서 src2를 뺌.
dst(I) = saturate( src1(I) – src2(I) ) if mask(I) != 0
- Threshold operation
double threshold (Mat src, Mat dst, double thresh, double maxval, int type)
특정한 픽셀의 값이 threshold를 기준으로 넘거나 못넘을 때 특정한 값으로 치환해줌.
- maxval : dst(I) = maxval if src(I) > thresh, 0 otherwise, when type is THRESH_BINARY
보통 grayscale같은 binary image(0혹은 1, 0혹은 255같이 두개의 값만 가지는)로 만드는 경우에 활용.
type 각 타입에 따라 결과영상 달라짐.
- Type : 각 타입에 따라 결과영상 달라짐 (Ex. THRESH_BINARY, THRESH_BINARY_INV, THRESH_TRUNC, THRESH_TOZERO, THRESH_TOZERO_INV)
- Threshold Binary : threshold 이상인 값들은 maxvalue, 다른 부분은 0으로 변환.
- Threshold Binary, inverted. : 반전. threshold 이상의 값들은 0, 다른부분은 maxval로 변환
- Truncate : threshold 이상의 값들은 다 threshold로 변환.
- Threshold to Zero : threshold 보다 작으면 0, 그렇지 않으면 원래값 유지
- Threshold to Zero, Inverted : 반전. 큰부분은 0으로 변환, 작으면 원래값 유지
int main() {
Mat image = imread("lena.jpg");
cvtColor(image, image, CV_BGR2GRAY);
Mat dst;
threshold(image, dst, 100, 255, THRESH_BINARY); // 100이상인 경우 255, 그렇지 않으면 0으로
imshow("dst", dst);
imshow("image", image);
waitKey(0);
return 0;
}
Void adaptiveThreshold(Mat src, Mat dst, double maxval, int adaptiveMethod, int thresholdType, int blockSize, double C)
각 픽셀마다 다른 threshold 적용. threshold는 주변의 픽셀 값으로 결정됨
- thresholdType : 주변 픽셀들의 평균으로 threshold 결정하는 방법(THRESH_BINARY), 주변 픽셀들의 가우시안.. 가중치 평균으로 결정하는 방법(THRESH_BINARY_INV)
- blockSize : 인접한 픽셀들의 평균, 혹은 가중치 평균으로 결정할 때. 그 인접한 범위를 결정하는 것. (3,5,7사용가능)
- double C : 특정한 상수값 C를 뺀 것을 threshold로 사용함
int main() {
Mat image = imread("lena.jpg");
cvtColor(image, image, CV_BGR2GRAY); //grayscale로 변환
Mat dst;
adaptiveThreshold(image, dst, 255, ADAPTIVE_THRESH_MEAN_C, THRESH_BINARY, 7, 10); // 인접한 픽셀들의 평균으로 threshold결정(상수 10빼줌), 인접픽셀 사이즈 7x7
imshow("dst", dst);
imshow("image", image);
waitKey(0);
return 0;
}
Void inRange(cv::InputArray src, cv::InputArray lowerb, cv::InputArray upperb, cv::OutputArray dst)
기준이 되는 값을 2개로 설정할 수 있고(lowerb, upperb), 그 사이의 있는 값에 대해서 255라는 값을 부여할 수 있다.
int main() {
Mat image = imread("hand.jpg");
cvtColor(image, image, CV_BGR2YCrCb); // 첫번째 채널값 Y, 두번째 Cr, 세번째 Cb임
inRange(image, Scalar(0, 133, 77), Scalar(255, 173, 127), image);
imshow("inRange", image);
waitKey(0);
return 0;
}
Mat convertTo(OutputArray m, int rtype, double alpha=1, double beta=0)
각각의 입력 array(rtpye)의 데이터 타입을 변환시켜주기 위해 활용하는 함수
m : 출력행렬
rtype : 원하는 출력행렬의 타입. rtype이 음수이면 출력행렬은 입력행렬과 같은 타입을 갖는다.
alpha, beta : 픽셀의 값을 변환시켜주고 싶을때 사용하는 파라미터로 alpha : 추가적으로 곱할 값, beta : 추가적으로 더할 값
converTo() 함수가 사용되는 경우?
- 일반적인 영상은 각 필셀 값을 uchar 자료형을 이용하여 표현한다. 이 때, 복잡한 연산을 수행할 경우 연산의 정확도를 높이기 위해 필셀 값을 uchar 같은 정수형이 아닌 float, doulbe형 같은 실수형으로 변한하여 내부 연산을 수행하기도 한다 (Ex. CV_8UC1 -> CV_32FC1로 변환)
- 또는 0~1 사이의 실수 값으로 구성된 2차원 행렬을 영상 형태로 화면에 나타내고 싶을 때, 행렬의 모든 원소에 255를 곱한 후 uchar 자료형으로 변환하여 그레이스케일 영상 형식으로 만든 후 화면에 출력한다.
Mat setTo(InputArray value, InputArray mask=noArray()
특정한 mat에 대해서 각각 mat 픽셀 값을 value값으로 치환시켜버리는 함수
특정한 범위(roi)에 대해서 수행하고 싶으면 두번째 파라미터 이용해 roi설정가능
Void convertScaleAbs(InputArray src, OutputArray dst, double alpha=1, double beta=0)
항상 dst타입은 uchar(unsigned char)로 결정됨.
src에 절대값을 취함. 나머지는 convertTo와 동일.
int main() {
Mat image = imread("lena.png");
Mat after_convertTo, after_convertScaleAbs;
imshow("original", image);
image.convertTo(after_convertTo, CV_16SC1); // 16비트 short?로 변환됨 채널1개.
imshow("after convertTo", after_convertTo); // imshow에서는 영상이 8비트라고 가정하기 때문에 회색조 영상 출력됨.
convertScaleAbs(image, after_convertScaleAbs, 2, 3); // 각각 픽셀에 2 곱하고 3 더함
imshow("after convertScaleAbs", after_convertScaleAbs);
image.setTo(Scalar(0)); // 입력영상을 scalar(0), 즉 0으로 설정 -> 검은화면 나옴
imshow("after setTo", image);
waitKey(0);
}
위의 코드를 돌렸을 때, after_converTo의 영상이 컬러영상으로 출력되는 것을 볼 수 있다.
분명 rtype 파라미터에 C1을 입력했음에도 불구하고, 흑백영상(채널1개)로 출력되지 않는다.
그래서 image.type() 함수를 통해 이미지의 픽셀 타입을 확인해 본 결과, covertTo()함수를 적용한 이미지들의 타입이 원본 이미지와 같이 모두 16인 걸 알 수 있다. 즉, converTo() 함수는 채널의 수와는 연관이 없다는 걸 알 수 있다.
/**
@file videocapture_basic.cpp
@brief A very basic sample for using VideoCapture and VideoWriter
@author PkLab.net
@date Aug 24, 2016
*/
#include <opencv2/opencv.hpp>
#include <stdio.h>
#include <string>
using namespace cv;
using namespace std;
int main() {
Mat image = imread("lena.png");
Mat after_convertTo, after_convertScaleAbs;
imshow("original", image);
image.convertTo(after_convertTo, CV_16SC1); // 16비트 short?로 변환됨 채널1개.
cout << "Image Type :" << image.type() << "\n";
imshow("after convertTo", after_convertTo); // imshow에서는 영상이 8비트라고 가정하기 때문에 회색조 영상 출력됨.
convertScaleAbs(image, after_convertScaleAbs, 2, 3); // 각각 픽셀에 2 곱하고 3 더함
cout << "Image Type :" << image.type() << "\n";
imshow("after convertScaleAbs", after_convertScaleAbs);
image.setTo(Scalar(0)); // 입력영상을 scalar(0), 즉 0으로 설정 -> 검은화면 나옴
cout << "Image Type :" << image.type() << "\n";
imshow("after setTo", image);
waitKey(0);
}
#define CV_USRTYPE1 (void)"CV_USRTYPE1 support has been dropped in OpenCV 4.0"
#define CV_CN_MAX 512
#define CV_CN_SHIFT 3
#define CV_DEPTH_MAX (1 << CV_CN_SHIFT)
#define CV_8U 0
#define CV_8S 1
#define CV_16U 2
#define CV_16S 3
#define CV_32S 4
#define CV_32F 5
#define CV_64F 6
#define CV_16F 7
#define CV_MAT_DEPTH_MASK (CV_DEPTH_MAX - 1)
#define CV_MAT_DEPTH(flags) ((flags) & CV_MAT_DEPTH_MASK)
#define CV_MAKETYPE(depth,cn) (CV_MAT_DEPTH(depth) + (((cn)-1) << CV_CN_SHIFT))
#define CV_MAKE_TYPE CV_MAKETYPE
#define CV_8UC1 CV_MAKETYPE(CV_8U,1)
#define CV_8UC2 CV_MAKETYPE(CV_8U,2)
#define CV_8UC3 CV_MAKETYPE(CV_8U,3)
#define CV_8UC4 CV_MAKETYPE(CV_8U,4)
#define CV_8UC(n) CV_MAKETYPE(CV_8U,(n))
#define CV_8SC1 CV_MAKETYPE(CV_8S,1)
#define CV_8SC2 CV_MAKETYPE(CV_8S,2)
#define CV_8SC3 CV_MAKETYPE(CV_8S,3)
#define CV_8SC4 CV_MAKETYPE(CV_8S,4)
#define CV_8SC(n) CV_MAKETYPE(CV_8S,(n))
#define CV_16UC1 CV_MAKETYPE(CV_16U,1)
#define CV_16UC2 CV_MAKETYPE(CV_16U,2)
#define CV_16UC3 CV_MAKETYPE(CV_16U,3)
#define CV_16UC4 CV_MAKETYPE(CV_16U,4)
#define CV_16UC(n) CV_MAKETYPE(CV_16U,(n))
#define CV_16SC1 CV_MAKETYPE(CV_16S,1)
#define CV_16SC2 CV_MAKETYPE(CV_16S,2)
#define CV_16SC3 CV_MAKETYPE(CV_16S,3)
#define CV_16SC4 CV_MAKETYPE(CV_16S,4)
#define CV_16SC(n) CV_MAKETYPE(CV_16S,(n))
#define CV_32SC1 CV_MAKETYPE(CV_32S,1)
#define CV_32SC2 CV_MAKETYPE(CV_32S,2)
#define CV_32SC3 CV_MAKETYPE(CV_32S,3)
#define CV_32SC4 CV_MAKETYPE(CV_32S,4)
#define CV_32SC(n) CV_MAKETYPE(CV_32S,(n))
#define CV_32FC1 CV_MAKETYPE(CV_32F,1)
#define CV_32FC2 CV_MAKETYPE(CV_32F,2)
#define CV_32FC3 CV_MAKETYPE(CV_32F,3)
#define CV_32FC4 CV_MAKETYPE(CV_32F,4)
#define CV_32FC(n) CV_MAKETYPE(CV_32F,(n))
#define CV_64FC1 CV_MAKETYPE(CV_64F,1)
#define CV_64FC2 CV_MAKETYPE(CV_64F,2)
#define CV_64FC3 CV_MAKETYPE(CV_64F,3)
#define CV_64FC4 CV_MAKETYPE(CV_64F,4)
#define CV_64FC(n) CV_MAKETYPE(CV_64F,(n))
#define CV_16FC1 CV_MAKETYPE(CV_16F,1)
#define CV_16FC2 CV_MAKETYPE(CV_16F,2)
#define CV_16FC3 CV_MAKETYPE(CV_16F,3)
#define CV_16FC4 CV_MAKETYPE(CV_16F,4)
#define CV_16FC(n) CV_MAKETYPE(CV_16F,(n))
참고로 type가 나오는 매크로를 확인해보면, 기본값(0~7) + ((채널-1)<<3)을 한 값이 나온다
타입이 16이면 CV_8UC3이다.
이 게시물은 한동대학교 황성수 교수님의 컴퓨터비전 강의를 공부하며 정리한 내용입니다.
'Computer Vision' 카테고리의 다른 글
[OpenCV] 메모리 관리 및 픽셀 액세스(Memory Management/Pixel Access) (0) | 2020.01.11 |
---|---|
[OpenCV] Drawing 함수(Drawing Function) (0) | 2020.01.11 |
[OpenCV] Open CV 기초 (0) | 2020.01.09 |
[컴퓨터 비전] 색공간의 이해 (0) | 2020.01.09 |
[컴퓨터 비전] 디지털 영상/ 동영상의 기초 (0) | 2020.01.09 |