cv::Mat img = cv::imread("lenna.bmp"); //이미지 불러오기. 흑백을 불러오려면 cv::IMREAD_GRAYSCALE 인자 추가.
if (img.empty())
{
std::cerr << "Image load failed!" << std::endl; // 이미지 load에 실패했을 경우 에러메세지 출력 후 종료.
return -1;
}
cv::namedWindow("Image"); // "Image"라는 이름의 window 생성
cv::imshow("Image", img); // "Image" 윈도우에 img를 출력.
cv::waitKey(0); // 키 입력을 입력한 시간(ms)만큼 기다림. 0초일 경우 무한대.
cv::destroyAllWindows();
return 0;
행렬 타입
CV_8U : 8bit unsigned integer (uchar)
CV_8S : 8bit signed integer (schar)
CV_16U: 16bit unsigned integer (ushort)
CV_16S: 16bit signed integer (short)
CV_32S: 32bit signed integer (int)
CV_32F: 32bit floating-point num (float)
CV_64F: 64bit floating-point num (double)
행렬 타입 뒷부분에 channel의 개수를 붙여준다. (e.g. CV_8UC3, CV_8UC1)
cv::Mat img = cv::imread("lenna.bmp");
std::cout << "Width: " << img.cols << std::endl;
std::cout << "Height: " << img.rows << std::endl;
std::cout << "Channels: " << img.channels() << std::endl;
if (img.type() == CV_8UC1)
std::cout << "img is a grayscale image" << std::endl;
else if(img.type() == CV_8UC3)
std::cout << "img is a truecolor image" << std::endl;
output:
Width: 512
Height: 512
Channels: 3
img is a truecolor image
행렬 초기화
cv::Mat img1; // 빈 행렬
cv::Mat img2(480, 640, CV_8UC1); // 480행, 640행, 1채널(grayscale), uchar
cv::Mat img3(cv::Size(640, 480), CV_8UC3); // width=640, height=480, 3채널, uchar
cv::Mat img4(480, 640, CV_8UC1, cv::Scalar(128)); // 행렬 값 128로 초기화.
cv::Mat img5(480, 640, CV_8UC3, cv::Scalar(0, 0, 255)); // 행렬 값 0, 0, 255 (red)로 초기화.
cv::Mat mat1 = cv::Mat::zeros(3, 3, CV_32SC1); // 3 by 3, 1 channel, short, zero mat
cv::Mat mat2 = cv::Mat::ones(3, 3, CV_32FC1); // 3 by 3, 1 channel, float, one matrix
cv::Mat mat3 = cv::Mat::eye(3, 3, CV_64FC1); // 3 by 3, 1 channel, double, identity matrix.
float data[] = {1, 2, 3, 4, 5, 6};
cv::Mat mat4(2, 3, CV_32FC1, data);
std::vector<float> vec1 = {1, 2, 3, 4, 5, 6};
cv::Mat mat5(2, 3, CV_32FC1, vec1.data());
cv::Mat mat6 = (cv::Mat_<float>(2, 3) << 1, 2, 3, 4, 5, 6);
cv::Mat mat7 = cv::Mat_<uchar>({2, 3}, {1, 2, 3, 4, 5, 6});
// 기존의 행렬을 지우고 새로운 행렬 생성.
img1.create(480, 640, CV_8UC1); // 초기화는 불가능.
img1 = cv::Scalar(255, 0, 0); // blue 컬러로 초기화.
img1.setTo(1.f); // 모든 값을 1.0으로 초기화.
행렬 참조, 복사
cv::Mat img1 = cv::imread("dog.bmp");
// 참조
cv::Mat img2 = img1; // 초기화 cv::Mat img2(img1);과 같다.
cv::Mat img3;
img3 = img1; // 대입
// 복사
cv::Mat img4 = img1.clone();
cv::Mat img5;
img1.copyTo(img5);
부분 영상 추출(image crop), ROI
cv::Mat img1 = cv::imread("cat.bmp");
cv::Mat img2 = img1(cv::Rect(220, 120, 340, 240)); // 참조
cv::Mat img3 = img1(cv::Rect(220, 120, 340, 240)); // 복사
행렬 픽셀 값 접근하기
cv::Mat mat1 = cv::Mat::zeros(10, 15, CV_8UC1);
// cv::Mat::at()
for(int y = 0; y < mat1.rows; y++)
{
for(int x = 0; x < mat1.cols; x++)
{
mat1.at<uchar>(y, x) += 5;
}
}
// cv::Mat::ptr()
for(int y = 0; y < mat1.rows; y++)
{
uchar* p = mat1.ptr<uchar>(y);
for (int x = 0; x < mat1.cols; x++)
{
p[x] += 5;
}
}
// cv::MatIterator_<T>
for(cv::MatIterator_<uchar> it = mat1.begin<uchar>();
it != mat1.end<uchar>(); ++it)
{
(*it) += 5;
}
// truecolor image 참조
cv::Mat mat2 = cv::Mat::zeros(3, 3, CV_8UC3);
for(int y = 0; y < mat1.rows; y++)
{
for(int x = 0; x < mat1.cols; x++)
{
cv::Vec3b& pixel = mat2.at<cv::Vec3b>(y, x);
pixel[0] = 0; R
pixel[1] = 255; G
pixel[2] = 255; B
}
}
행렬 기본연산
float data[] = {1, 2, 3, 4};
cv::Mat mat1(2, 2, CV_32FC1, data);
cv::Mat mat2 = mat1.inv();
std::cout << "mat1:\n" << mat1 << std::endl;
std::cout << "mat2 = mat1 inverse:\n" << mat2 << std::endl;
std::cout << "mat1.t():\n" << mat1.t() << std::endl;
std::cout << "mat1 + 3:\n" << mat1 + 3 << std::endl;
std::cout << "mat1 + mat2:\n" << mat1 + mat2 << std::endl;
std::cout << "mat1 * mat2:\n" << mat1 * mat2 << std::endl;
output:
mat1:
[1, 2;
3, 4]
mat2 = mat1 inverse:
[-2, 1;
1.5, -0.5]
mat1.t():
[1, 3;
2, 4]
mat1 + 3:
[4, 5;
6, 7]
mat1 + mat2:
[-1, 3;
4.5, 3.5]
mat1 * mat2:
[1, 0;
0, 1]
카메라, 동영상 불러와서 출력하기
cv::VideoCapture cap("test_video.mp4");
//cv::VideoCapture cap(0); // camera
if (!cap.isOpened())
{
std::cerr << "Camera open failed" << std::endl;
return -1;
}
cv::Mat frame;
while(cv::waitKey(1) != 'q')
{
cap >> frame;
if (frame.empty())
{
std::cerr << "Frame empty!" << std::endl;
break;
}
cv::imshow("frame", frame);
}
카메라, 동영상 속성 값 참조와 설정
cv::VideoCaptureProperties
CAP_PROP_FRAME_WIDTH : 프레임 가로 크기
CAP_PROP_FRAME_HEIGHT : 프레임 세로 크기
CAP_PROP_FRAME_COUNT : 비디오 파일 총 프레임 수
CAP_PROP_FPS : 초당 프레임
CAP_PROP_POS_FRAMES : 현재 프레임 번호
CAP_PROP_EXPOSURE : 노출
...
cv::VideoCapture cap("test_video.mp4");
//cv::VideoCapture cap(0); // camera
if (!cap.isOpened())
{
std::cerr << "Camera open failed" << std::endl;
return -1;
}
int width = cvRound(cap.get(cv::CAP_PROP_FRAME_WIDTH)); // 속성 get
int height = cvRound(cap.get(cv::CAP_PROP_FRAME_HEIGHT));
double fps = cap.get(cv::CAP_PROP_FPS);
cap.set(cv::CAP_PROP_FPS, 40); // 속성 set
동영상 저장하기, VideoWriter
fourcc : 압축 방식을 나타냄.
cv::VideoCapture cap("test_video.mp4");
int fourcc = cv::VideoWriter::fourcc('X', 'V', 'I', 'D');
double fps = 15;
int w = (int)cap.get(cv::CAP_PROP_FRAME_WIDTH);
int h = (int)cap.get(cv::CAP_PROP_FRAME_HEIGHT);
cv::Size sz(w, h);
cv::VideoWriter outputVideo("output.avi", fourcc, fps, sz); // 마지막 인자로 false 추가 시 grayscale로 저장.
int delay = cvRound(1000 / fps);
cv::Mat frame;
while(true)
{
cap >> frame;
outputVideo << frame;
cv::imshow("frame", frame);
if (cv::waitKey(delay) == 'q')
break;
}
cv::Mat img(480, 640, CV_8UC3, cv::Scalar(0, 0, 0));
cv::Scalar red(0, 0, 255), green(0, 255, 0),blue(255, 0, 0);
int thickness = 1;
// line
cv::Point pt1(50, 50), pt2(100, 100);
cv::line(img, pt1, pt2, red, 1, cv::LINE_AA); // LINE_4, LINE_8, LINE_AA 중 지정.
// rectangle
cv::Rect rec(200, 150, 100, 50); // x, y, width, height
cv::rectangle(img, rec, blue, thickness, cv::LINE_8); // thickness=-1일 경우 내부가 색칠된 사각형.
// polylines
std::vector<cv::Point> pts = {cv::Point(400, 200), cv::Point(400, 400), cv::Point(300, 300)};
cv::polylines(img, pts, true, green, thickness, cv::LINE_8); // 3번째 인자로 true를 넣으면 폐곡선.
// text
cv::Point text_left_bottom(0, 450);
int font = cv::HersheyFonts::FONT_HERSHEY_SIMPLEX;
cv::putText(img, "putText", text_left_bottom, font, 1.0, red, thickness, cv::LINE_AA);
cv::imshow("img", img);
while(cv::waitKey(0) != 'q')
continue;
MouseEventTypes, MouseEventFlags
enum MouseEventTypes
{
EVENT_MOUSEMOVE = 0,
EVENT_LBUTTONDOWN = 1,
EVENT_RBUTTONDOWN = 2,
EVENT_MBUTTONDOWN = 3,
EVENT_LBUTTONUP = 4,
EVENT_RBUTTONUP = 5,
EVENT_MBUTTONUP = 6,
EVENT_LBUTTONDBLCLK = 7,
EVENT_RBUTTONDBLCLK = 8,
EVENT_MBUTTONDBLCLK = 9,
EVENT_MOUSEWHEEL = 10,
EVENT_MOUSEHWHEEL = 11,
};
enum MouseEventFlags // 2배씩 증가하므로 상태의 중첩이 가능하다.
{
EVENT_FLAG_LBUTTON = 1, // LBUTTON이 눌린 상태인지
EVENT_FLAG_RBUTTON = 2,
EVENT_FLAG_MBUTTON = 4,
EVENT_FLAG_CTRLKEY = 8,
EVENT_FLAG_SHIFTKEY = 16,
EVENT_FLAG_ALTKEY = 32,
};
마우스 이벤트, 플래그 예시
void my_mouse_callback(int event, int x, int y, int flags, void*)
{
switch (event)
{
case cv::EVENT_LBUTTONDOWN:
std::cout << "EVENT_LBUTTONDOWN" << x << ", " << y << std::endl;
break;
case cv::EVENT_MOUSEMOVE:
if (flags & cv::EVENT_FLAG_LBUTTON || flags & cv::EVENT_FLAG_CTRLKEY)
std::cout << "EVENT_MOUSEMOVE: " << x << ", " << y << std::endl;
break;
default:
break;
}
}
int main()
{
cv::Mat src = cv::imread("lenna.bmp");
cv::namedWindow("src");
cv::setMouseCallback("src", my_mouse_callback);
cv::imshow("src", src);
while(cv::waitKey(0) != 'q')
continue;
return 0;
}
L버튼 클릭 시
output:
EVENT_LBUTTONDOWN316, 225
EVENT_LBUTTONDOWN312, 263
EVENT_LBUTTONDOWN433, 315
EVENT_LBUTTONDOWN218, 229
EVENT_LBUTTONDOWN216, 247
L버튼 또는 ctrl을 누른 채 드래그 시
output:
EVENT_MOUSEMOVE: 246, 245
EVENT_MOUSEMOVE: 246, 246
EVENT_MOUSEMOVE: 247, 246
EVENT_MOUSEMOVE: 247, 246
EVENT_MOUSEMOVE: 247, 247
EVENT_MOUSEMOVE: 247, 248
EVENT_MOUSEMOVE: 247, 248
EVENT_MOUSEMOVE: 247, 249
이미지 위에 그림 그리기
cv::Point pt_prev;
void my_mouse_callback2(int event, int x, int y, int flags, void* userdata)
{
switch (event)
{
case cv::EVENT_LBUTTONDOWN:
pt_prev = cv::Point(x, y);
break;
case cv::EVENT_MOUSEMOVE:
if(flags & cv::EVENT_FLAG_LBUTTON)
{
cv::Mat img = *(cv::Mat*)userdata;
cv::line(img, pt_prev, cv::Point(x, y), cv::Scalar(0, 255, 255), 3, cv::LINE_AA);
pt_prev = cv::Point(x, y);
cv::imshow("src", img);
}
default:
break;
}
}
int main()
{
cv::Mat src = cv::imread("lenna.bmp");
cv::namedWindow("src");
cv::setMouseCallback("src", my_mouse_callback2, (void*)&src);
cv::imshow("src", src);
while(cv::waitKey(0) != 'q')
continue;
return 0;
}
L버튼 또는 Ctrl키를 누른 채로 마우스를 움직이면 line이 그려진다.
void my_trackbar_callback(int pos, void* userdata)
{
cv::Mat img = *(cv::Mat*)userdata;
img.setTo(pos * 16);
cv::imshow("src", img);
}
int main()
{
cv::Mat src = cv::Mat::zeros(400, 400, CV_8UC1);
cv::namedWindow("src");
int tb_loc; // 트랙바 위치를 받아올 수 있다.
int tb_max = 16; // 트랙바의 최대값을 설정 (최소는 0으로 고정)
cv::createTrackbar("level", "src", &tb_loc, tb_max, my_trackbar_callback, (void*)&src);
while(cv::waitKey(0) != 'q')
continue;
return 0;
}
uchar data[] = {1, 2, 3, 4, 5, 6};
cv::Mat mat1(2, 3, CV_8UC1, data);
// mask 행렬을 인자로 추가 시 mask의 원소가 0인 부분은 제외하고 연산.
int sum1 = (int)cv::sum(mat1)[0]; // 21
double mean1 = cv::mean(mat1)[0]; // 10.5
double minVal, maxVal; // 1, 6
cv::Point minPoint, maxPoint; // [0, 0], [2, 1]
cv::minMaxLoc(mat1, &minVal, &maxVal, &minPoint, &maxPoint);
자료형 변환
cv::Mat img = cv::imread("lenna.bmp", cv::IMREAD_GRAYSCALE);
cv::Mat dst;
double alpha(1), beta(0); // 각 픽셀의 값이 val -> saturate(alpha*val + beta)로 변환.
img.convertTo(dst, CV_32FC1, alpha, beta); //
행렬의 정규화(원소 값 범위 정규화)
cv::Mat img = cv::imread("lenna.bmp", cv::IMREAD_GRAYSCALE);
cv::Mat dst;
double alpha = 0; // 목표 norm값. (NORM_MINMAX타입인 경우 최솟값을 의미함.)
double beta = 255; // NORM_MINMAX타입인 경우에 사용되며 최대값을 의미함.
int norm_type = cv::NORM_MINMAX; // 정규화 타입. NORM_L2, NORM_INF, NORM_L1, NORM_MINMAX
cv::normalize(img, dst, alpha, beta, norm_type);
색 변환
COLOR_BGR2GRAY / COLOR_GRAY2BGR
COLOR_BGR2HSV / COLOR_HSV2BGR
COLOR_BGR2YCrCb/ COLOR_YCrCb2BGR
...
cv::Mat src = cv::imread("lenna.bmp");
cv::Mat dst;
cv::cvtColor(src, dst, cv::COLOR_BGR2GRAY);
cv::Mat src = cv::imread("lenna.bmp");
std::vector<cv::Mat> planes;
cv::split(src, planes);
std::swap(planes[0], planes[2]); // BGR 순서에서 RGB 순서가 됨.
cv::Mat dst;
cv::merge(planes, dst);
cv::imshow("src", src);
cv::imshow("dst", dst);
while(cv::waitKey(0) != 'q')
continue;
cv::TickMeter tm;
tm.start();
// my function
tm.stop();
std::cout << "my function: " << tm.getTimeMilli() << "ms." << std::endl;
tm.reset(); // reset 빼먹으면 위에서 걸린 시간도 추가됨.
tm.start();
// my function
tm.stop();
std::cout << "my function: " << tm.getTimeMilli() << "ms." << std::endl;
cv::Mat src = cv::imread("airplane.bmp", cv::IMREAD_COLOR);
cv::Mat mask = cv::imread("mask_plane.bmp", cv::IMREAD_GRAYSCALE);
cv::Mat dst = cv::imread("field.bmp", cv::IMREAD_COLOR);
if(src.empty() || mask.empty() || dst.empty())
{
std::cerr << "Image load failed!" << std::endl;
return -1;
}
src.copyTo(dst, mask); // copyTo 외에도 mask 연산을 지원하는 다양한 함수가 존재함.
cv::imshow("src", src);
cv::imshow("dst", dst);
cv::imshow("mask", mask);
while(cv::waitKey(0) != 'q')
continue;
단순 덧셈
cv::Mat src = cv::imread("lenna.bmp", cv::IMREAD_GRAYSCALE);
if (src.empty())
{
std::cerr << "Image load failed!" << std::endl;
return -1;
}
cv::Mat dst = src + 50; // 50 -> cv::Scalar(50)으로 변환됨.
// dst = src - 50;
// cv::add(src, 50, dst); 같은 결과.
cv::imshow("src", src);
cv::imshow("dst", dst);
while(cv::waitKey(0) != 'q')
continue;
반전
cv::Mat src = cv::imread("lenna.bmp", cv::IMREAD_GRAYSCALE);
if (src.empty())
{
std::cerr << "Image load failed!" << std::endl;
return -1;
}
//cv::Mat dst = 255 - src;
cv::Mat dst = ~src;
cv::imshow("src", src);
cv::imshow("dst", dst);
while(cv::waitKey(0) != 'q')
continue;
밝기 조절 구현
cv::Mat src = cv::imread("lenna.bmp", cv::IMREAD_GRAYSCALE);
if (src.empty())
{
std::cerr << "Image load failed!" << std::endl;
return -1;
}
cv::Mat dst(src.rows, src.cols, CV_8UC1);
for(int y = 0; y < src.rows; y++)
{
for (int x = 0; x < src.cols; x++)
{
//dst.at<uchar>(y, x) = src.at<uchar>(y, x) + 50;
dst.at<uchar>(y, x) = cv::saturate_cast<uchar>(src.at<uchar>(y, x) + 50);
}
}
cv::imshow("src", src);
cv::imshow("dst", dst);
while(cv::waitKey(0) != 'q')
continue;
saturate_cast를 하지 않으면 아래 그림처럼 된다.(픽셀 값이 255보다 크거나 0보다 작아지는 경우)
밝기 보정
cv::Mat src = cv::imread("tiffany.bmp", cv::IMREAD_GRAYSCALE);
if (src.empty())
{
std::cerr << "Image load failed!" << std::endl;
return -1;
}
// 평균 밝기를 128로 보정.
int m = cv::mean(src)[0];
cv::Mat dst1 = src + (128 - m);
cv::imshow("src", src);
cv::imshow("128", dst1);
while(cv::waitKey(0) != 'q')
continue;
cv::Mat src = cv::imread("columbia.bmp", cv::IMREAD_GRAYSCALE);
if (src.empty())
{
std::cerr << "Image load failed!" << std::endl;
return -1;
}
// 단순 contrast 조절
double alpha = 1.0;
cv::Mat dst1 = src + (src - 128) * alpha;
// mean을 이용한 contrast 조절
int m = (int)cv::mean(src)[0];
cv::Mat dst2 = src + (src - m) * alpha;
cv::imshow("src", src);
cv::imshow("dst1", dst1);
while(cv::waitKey(0) != 'q')
continue;
히스토그램 직접 구하기
cv::Mat src = cv::imread("columbia.bmp", cv::IMREAD_GRAYSCALE);
if (src.empty())
{
std::cerr << "Image load failed!" << std::endl;
return -1;
}
int hist[256] = {0, };
for(int y = 0; y < src.rows; y++)
{
for(int x = 0; x < src.cols; x++)
{
hist[src.at<uchar>(y, x)]++;
}
}
//histogram 그리기
int histMax = 0;
for(int i = 0; i < 256; i++)
{
if(hist[i] > histMax) histMax = hist[i];
}
printf("%d", histMax);
cv::Mat imgHist(100, 256, CV_8UC1, cv::Scalar(255));
for(int i = 0; i < 256; i++)
{
int y_ = 100 - cvRound(hist[i] * 100 / histMax);
cv::line(imgHist, cv::Point(i, 100), cv::Point(i, y_), cv::Scalar(0));
}
cv::imshow("src", src);
cv::imshow("hist", imgHist);
while(cv::waitKey(0) != 'q')
continue;
calcHist 함수 이용
함수 인자
1. const Mat images : 입력 영상의 배열 또는 영상의 주소.
2. int nimages : 입력 영상의 개수
3. const int channels: 정수형 배열. 히스토그램을 구할 이미지의 채널들.
4. InptArray mask
5. OutputArray hist : 히스토그램 이미지 출력.
6. int dims : 출력 히스토그램 차원.
7. const int* histSize: 히스토그램 각 차원의 크기를 나타내는 배열.
8. const float** ranges: 히스토그램 각 차원별 최대, 최소값을 원소로 갖는 배열의 배열.
cv::Mat calcGrayHist(const cv::Mat& img)
{
CV_Assert(img.type() == CV_8UC1);
cv::Mat hist;
int channels[] = {0};
int dims = 1;
const int histSize[] = {256};
float graylevel[] = {0, 256};
const float* ranges[] = {graylevel};
cv::calcHist(&img, 1, channels, cv::Mat(), hist, dims, histSize, ranges);
return hist;
}
cv::Mat getGrayHistImage(const cv::Mat& hist)
{
CV_Assert(hist.type() == CV_32FC1);
CV_Assert(hist.size() == cv::Size(1, 256));
double histMax = 0.;
minMaxLoc(hist, 0, &histMax);
cv::Mat imgHist(100, 256, CV_8UC1, cv::Scalar(255));
for(int i = 0; i < 256; i++)
{
int y_ = 100 - cvRound(hist.at<float>(i) * 100 / histMax);
cv::line(imgHist, cv::Point(i, 100), cv::Point(i, y_), cv::Scalar(0));
}
return imgHist;
}
int main()
{
cv::Mat src = cv::imread("lenna.bmp", cv::IMREAD_GRAYSCALE);
cv::Mat hist = calcGrayHist(src);
cv::Mat imgHist = getGrayHistImage(hist);
cv::imshow("src", src);
cv::imshow("hist", imgHist);
while(cv::waitKey(0) != 'q')
continue;
return 0;
}
결과는 같다.
cv::Mat src = cv::imread("lenna.bmp", cv::IMREAD_GRAYSCALE);
double gmin, gmax;
cv::minMaxLoc(src, &gmin, &gmax);
cv::Mat dst = (src - gmin) * 255 / (gmax - gmin);
// cv::normalize(src, dst, 0, 255, cv::NORM_MINMAX); // 같은 결과를 얻음.
cv::Mat hist = calcGrayHist(src);
cv::Mat imgHist = getGrayHistImage(hist);
cv::Mat hist2 = calcGrayHist(dst);
cv::Mat imgHist2 = getGrayHistImage(hist2);
cv::imshow("src", src);
cv::imshow("dst", dst);
cv::imshow("hist", imgHist);
cv::imshow("hist2", imgHist2);
while(cv::waitKey(0) != 'q')
continue;
cv::Mat src = cv::imread("lenna.bmp", cv::IMREAD_GRAYSCALE);
cv::Mat dst(src.rows, src.cols, src.type());
int hist_[256] = {0, };
for(int y = 0; y < src.rows; y++)
{
for(int x = 0; x < src.cols; x++)
{
hist_[src.at<uchar>(y, x)]++;
}
}
float cdf[256] = {};
for(int i = 1; i < 256; i++)
{
cdf[i] = cdf[i-1] + float(hist_[i] / float(src.total()));
}
for(int y = 0; y < src.rows; y++)
{
for (int x = 0; x < src.cols; x++)
{
dst.at<uchar>(y, x) = uchar(cdf[src.at<uchar>(y, x)] * 255);
}
}
//cv::equalizeHist(src, dst); // 같은 결과
cv::Mat hist = calcGrayHist(src);
cv::Mat imgHist = getGrayHistImage(hist);
cv::Mat hist2 = calcGrayHist(dst);
cv::Mat imgHist2 = getGrayHistImage(hist2);
cv::imshow("src", src);
cv::imshow("dst", dst);
cv::imshow("hist_src", imgHist);
cv::imshow("hist_dst", imgHist2);
while(cv::waitKey(0) != 'q')
continue;
cv::Mat src1 = cv::imread("lenna256.bmp", cv::IMREAD_GRAYSCALE);
cv::Mat src2 = cv::imread("square.bmp", cv::IMREAD_GRAYSCALE);
if (src1.empty() || src2.empty())
{
std::cerr << "Image load failed!" << std::endl;
return -1;
}
if(src1.size() != src2.size() || src1.type() != src2.type())
{
std::cerr << "Images are different in size or type!" << std::endl;
return -1;
}
cv::Mat dst1, dst2, dst3, dst4;
cv::add(src1, src2, dst1); // 덧셈
cv::subtract(src1, src2, dst2); // 뺄셈
cv::addWeighted(src1, 0.5, src2, 0.5, 0.0, dst3); // 가중합
cv::absdiff(src1, src2, dst4); // 차이
cv::imshow("src1", src1);
cv::imshow("src2", src2);
cv::imshow("add", dst1);
cv::imshow("subtract", dst2);
cv::imshow("addWeighted", dst3);
cv::imshow("absdiff", dst4);
while(cv::waitKey(0) != 'q')
continue;
cv::Mat src1 = cv::imread("lenna256.bmp", cv::IMREAD_GRAYSCALE);
cv::Mat src2 = cv::imread("square.bmp", cv::IMREAD_GRAYSCALE);
if (src1.empty() || src2.empty())
{
std::cerr << "Image load failed!" << std::endl;
return -1;
}
if(src1.size() != src2.size() || src1.type() != src2.type())
{
std::cerr << "Images are different in size or type!" << std::endl;
return -1;
}
cv::Mat dst1, dst2, dst3, dst4;
cv::bitwise_and(src1, src2, dst1);
cv::bitwise_or(src1, src2, dst2);
cv::bitwise_xor(src1, src2, dst3);
cv::bitwise_not(src1, dst4); // dst4 = ~src1; 과 같음.
cv::imshow("src1", src1);
cv::imshow("src2", src2);
cv::imshow("and", dst1);
cv::imshow("or", dst2);
cv::imshow("xor", dst3);
cv::imshow("not src1", dst4);
while(cv::waitKey(0) != 'q')
continue;
엠보싱 필터
cv::Mat src = cv::imread("lenna.bmp", cv::IMREAD_GRAYSCALE);
if(src.empty())
{
std::cerr << "Image load failed!" << std::endl;
return -1;
}
float data[] = {-1, -1, 0, -1, 0, 1, 0, 1, 1};
cv::Mat kernel(3, 3, CV_32FC1, data);
cv::Mat dst;
cv::filter2D(src, dst, -1, kernel, cv::Point(-1, -1), 128);
//6번째 인자에 128을 넣어 전체 이미지에 128만큼 더해준다.
cv::imshow("src", src);
cv::imshow("dst", dst);
while(cv::waitKey(0) != 'q')
continue;
평균값 필터
cv::Mat src = cv::imread("lenna.bmp", cv::IMREAD_GRAYSCALE);
if(src.empty())
{
std::cerr << "Image load failed!" << std::endl;
return -1;
}
std::vector<float> data(9, 1.f/9.f);
cv::Mat kernel(3, 3, CV_32FC1, data.data());
// cv::Mat kernel = cv::Mat::ones(3, 3, CV_32FC1) / 9.f; // 같은결과.
cv::Mat dst;
cv::filter2D(src, dst, -1, kernel);
// cv::blur(src, dst, cv::Size(3, 3)); // 블러 함수를 사용할 수도 있다.
cv::imshow("src", src);
cv::imshow("dst", dst);
while(cv::waitKey(0) != 'q')
continue;
가우시안 블러
cv::Mat src = cv::imread("lenna.bmp", cv::IMREAD_GRAYSCALE);
if(src.empty())
{
std::cerr << "Image load failed!" << std::endl;
return -1;
}
cv::Mat dst;
cv::GaussianBlur(src, dst, cv::Size(), 2.0);
// sigma = 1.0일 경우 커널사이즈는 7x7이고, sigma가 커질수록 커널사이즈도 늘어나고, 연산량도 늘어남.
cv::imshow("src", src);
cv::imshow("dst", dst);
while(cv::waitKey(0) != 'q')
continue;
평균값 필터를 이용한 언샤프 마스크 필터링
cv::Mat src = cv::imread("rose.bmp", cv::IMREAD_GRAYSCALE);
if(src.empty())
{
std::cerr << "Image load failed!" << std::endl;
return -1;
}
cv::Mat dst, blr;
std::vector<float> data(9, -1.f/9.f);
data[4] = 17.f/9.f;
cv::Mat kernel(3, 3, CV_32FC1, data.data());
cv::filter2D(src, dst, -1, kernel);
//cv::blur(src, blr, cv::Size(3, 3));
//dst = 2 * src - blr; // 같은 결과를 얻는다.
cv::imshow("src", src);
cv::imshow("dst", dst);
while(cv::waitKey(0) != 'q')
continue;
가우시안 블러를 이용한 언샤프 마스크 필터링, 가중치 alpha 도입
cv::Mat src = cv::imread("rose.bmp", cv::IMREAD_GRAYSCALE);
if(src.empty())
{
std::cerr << "Image load failed!" << std::endl;
return -1;
}
cv::Mat srcf;
src.convertTo(srcf, CV_32FC1);
cv::Mat blr;
cv::GaussianBlur(srcf, blr, cv::Size(), 1.0);
float alpha = 1.f;
cv::Mat dst = (1.f + alpha) * srcf - alpha * blr;
dst.convertTo(dst, CV_8UC1);
cv::imshow("src", src);
cv::imshow("dst", dst);
while(cv::waitKey(0) != 'q')
continue;
가우시안 잡음 생성
cv::Mat src = cv::imread("lenna.bmp", cv::IMREAD_GRAYSCALE);
cv::Mat noise(src.size(), CV_32S);
cv::randn(noise, 0, 10);
cv::Mat dst;
cv::add(src, noise, dst, cv::noArray(), CV_8U);
cv::imshow("src", src);
cv::imshow("dst", dst);
while(cv::waitKey(0) != 'q')
continue;
프로파일 출력 프로그램
cv::Mat src_, dst_, profile_;
int row_ = 0;
void on_trackbar(int, void*)
{
src_.copyTo(dst_);
profile_.setTo(255);
uchar* pSrc = (uchar*)src_.ptr<uchar>(row_);
uchar* pDst = (uchar*)dst_.ptr<uchar>(row_);
for(int i = 1; i < src_.cols; i++)
{
cv::line(profile_, cv::Point(i-1, 255-pSrc[i-1]), cv::Point(i, 255-pSrc[i]), 0);
pDst[i] = cv::saturate_cast<uchar>(pSrc[i] + 50);
}
cv::imshow("dst", dst_);
cv::imshow("profile", profile_);
}
int main()
{
src_ = cv::imread("lenna.bmp", cv::IMREAD_GRAYSCALE);
if(src_.empty())
{
std::cerr << "Image load failed!" << std::endl;
return -1;
}
cv::namedWindow("dst");
cv::namedWindow("profile");
profile_.create(256, src_.cols, CV_8U);
cv::createTrackbar("Profile", "dst", &row_, src_.rows - 1, on_trackbar);
on_trackbar(0, 0);
while(cv::waitKey(0) != 'q')
continue;
return 0;
}
양방향 필터
에지는 보존하고 잡음을 제거하는 필터. 가우시안 블러에 비해 실행시간이 크다.
cv::Mat src = cv::imread("lenna.bmp", cv::IMREAD_GRAYSCALE);
if(src.empty())
{
std::cerr << "Image load failed!" << std::endl;
return -1;
}
cv::Mat dst1, dst2;
cv::GaussianBlur(src, dst1, cv::Size(), 5.0);
cv::bilateralFilter(src, dst2, -1, 10, 5);
cv::imshow("src", src);
cv::imshow("gaussian", dst1);
cv::imshow("bilateral", dst2);
while(cv::waitKey(0) != 'q')
continue;
이미지의 픽셀의 최대, 최소값 대신 1%, 99% 값을 이용해 스트레칭.
#include <opencv2/opencv.hpp>
#include <iostream>
void histogram_stretching_mod(const cv::Mat& src, cv::Mat& dst);
int main()
{
cv::Mat src = cv::imread("lenna.bmp", cv::IMREAD_GRAYSCALE);
cv::Mat dst;
histogram_stretching_mod(src, dst);
cv::imshow("src", src);
cv::imshow("dst", dst);
while(cv::waitKey(0) != 'q')
continue;
return 0;
}
void histogram_stretching_mod(const cv::Mat& src, cv::Mat& dst)
{
int hist[256] = {0, };
for(int y = 0; y < src.rows; y++)
{
const uchar* p = src.ptr<uchar>(y);
for(int x = 0; x < src.cols; x++)
{
hist[p[x]]++;
}
}
int gmin = 255, gmax = 0;
int ratio = int(src.cols * src.rows * 0.01);
for(int i = 0, s = 0; i <= 255; i++)
{
s += hist[i];
if (s > ratio)
{
gmin = i;
break;
}
}
for(int i = 255, s = 0; i >= 0; i--)
{
s += hist[i];
if (s > ratio)
{
gmax = i;
break;
}
}
dst = (src - gmin) * 255 / (gmax - gmin);
//cv::normalize(src, dst, gmin, gmax, cv::NORM_MINMAX);
}