#include <opencv2 / improc.hpp>
#include <opencv2 / highgui.hpp>
#include <opencv2 / imgproc /imgproc.hpp>
#include <iostream>

using namespace cv;
using namespace std;

int main()
{
	Mat src, dst, cdst;
    src = imread("KN_building.jpg", IMREAD_GRAYSCALE);
    
    Canny(src, dst, 35, 255);
    cvtColor(dst, cdst, COLOR_GRAY2BGR);
    vector <Vec4i> lines;
    HoughLinesP(dst, lines, 1, CV_PI / 180, 50, 100, 20);
    for(size_t i = 0; i< lines.size(); i++){
    	Vec4i l = lines[i];
        line(cdst, Point(l[0], l[1]), Point(l[2], l[3]), Scalar(0, 0, 255), 2);
    }
    
    imshow("허프변환",cdst);
    
    waitkey(0);
}

 

#include <opencv2/improc.hpp>
#include <opencv/highgui.hpp>
#include <iostream>

using namespace cv;
using namespace std;

int main()
{
	Mat src, dst1, dst2, dst3, dst4, 
    src = imread("disney.jpg", IMREAD_GRAYSCALE);
    
    threshold(src, dst1, 127, 255, THRESH_BINARY);
    
    Mat saltpepper_noise = Mat::zeros(src.rows, src.cols, CV_8U);
    randu(saltpepper_noise, 0, 255);
    
    Mat black = saltpepper_noise < 30;
    Mat white = saltpepper_noise > 225;
    
    Mat saltpepper_img = dst1.clone();
    saltpepper_img.setTo(255, white);
    saltpepper_img.setTo(0, black);
    
    imshow("1단계: 소금후추 노이즈 적용", saltpepper_img);
    
    Mat element = getStructuringElement(MORPH_ELLIPSE, Size(3,3));
    morphologyEx(saltpepper_img, dst2, MORPH_CLOSE, element);
    imshow("2단계: 모폴로지 닫힘 연산 적용", dst3);
    
    morphologyEx(dst2, dst3, MORPH_OPEN, element);
    imshow("3단계: 모폴로지 열림 적용", dst4);
    
    erode(dst3, dst4, element);
    imshow("4단계: 침식 적용", dst5);
    
    waitKey(0);
}

 

#include <opencv2 /imgproc.hpp>
#include <opencv2 /highgui.hpp>
#include <iostream>
using namespace cv;
using namespace std;

Mat src_gray;
Mat dst, detected_edges; 
int lowThreshold = 50;
int highThreshold = 140;

int main()
{
	Mat src = imread("dog.png", IMREAD_COLOR); 
    if(src.empty())
    {
    	cout << "Could not open or find the image\n" << endl;
        return -1;
    }
    cvtColor(src, src_gray, COLOR_BGR2GRAY);
    blur(src_gray, detected_edges, Size(3, 3));
    Canny(detected_edges, detected_edges, lowThreshold, highThreshold, 3);
    namedWindow("Canny Edge", WINDOW_AUTOSIZE);
    imshow("Canny Edge", detected_edges);
    
    waitKey(0);
    return 0;
}

 

 

#include <opencv2/opencv.hpp>
#include <opencv2/highgui.hpp>
#include <iostream>
using namespace cv;
using namespace std;


void maded_prewitt(const Mat& image, Mat& result, int thresh) {

	// 수직마스크
	Mat maskX = (Mat_<double>(3, 3) << -1, 0, 1, -1, 0, 1, -1, 0, 1);
	// 수평마스크
	Mat maskY = (Mat_<double>(3, 3) << 1, 1, 1, 0, 0, 0, -1, -1, -1);

	int filterOffset = 3 / 2;

	result = Mat::zeros(image.rows - filterOffset * 2, image.cols - filterOffset * 2, image.type());

	double sumEdgeX;
	double sumEdgeY;
	double magnitude;

	for (int yimage = filterOffset; yimage < image.rows - filterOffset; ++yimage) {
		for (int ximage = filterOffset; ximage < image.cols - filterOffset; ++ximage) {

			sumEdgeX = 0;
			sumEdgeY = 0;
			for (int ymask = -filterOffset; ymask <= filterOffset; ++ymask) {
				for (int xmask = -filterOffset; xmask <= filterOffset; ++xmask) {
					sumEdgeX += image.at<uchar>(yimage + ymask, ximage + xmask) * maskX.at<double>(filterOffset + ymask, filterOffset + xmask);
					sumEdgeY += image.at<uchar>(yimage + ymask, ximage + xmask) * maskY.at<double>(filterOffset + ymask, filterOffset + xmask);
				}
			}
			magnitude = sqrt(pow(sumEdgeY, 2) + pow(sumEdgeX, 2));
			result.at<uchar>(yimage - filterOffset, ximage - filterOffset) = ((magnitude > thresh) ? 255 : 0);
		}
	}

}

void maded_sobel(const Mat& image, Mat& result, int thresh) {

	// 수직마스크
	Mat maskX = (Mat_<double>(3, 3) << -1, 0, 1, -2, 0, 2, -1, 0, 1);
	// 수평마스크
	Mat maskY = (Mat_<double>(3, 3) << 1, 2, 1, 0, 0, 0, -1, -2, -1);

	int filterOffset = 3 / 2;

	result = Mat::zeros(image.rows - filterOffset * 2, image.cols - filterOffset * 2, image.type());

	double sumEdgeX;
	double sumEdgeY;
	double magnitude;

	for (int yimage = filterOffset; yimage < image.rows - filterOffset; ++yimage) {
		for (int ximage = filterOffset; ximage < image.cols - filterOffset; ++ximage) {

			sumEdgeX = 0;
			sumEdgeY = 0;
			for (int ymask = -filterOffset; ymask <= filterOffset; ++ymask) {
				for (int xmask = -filterOffset; xmask <= filterOffset; ++xmask) {
					sumEdgeX += image.at<uchar>(yimage + ymask, ximage + xmask) * maskX.at<double>(filterOffset + ymask, filterOffset + xmask);
					sumEdgeY += image.at<uchar>(yimage + ymask, ximage + xmask) * maskY.at<double>(filterOffset + ymask, filterOffset + xmask);
				}
			}
			magnitude = sqrt(pow(sumEdgeY, 2) + pow(sumEdgeX, 2));
			result.at<uchar>(yimage - filterOffset, ximage - filterOffset) = ((magnitude > thresh) ? 255 : 0);
		}
	}

}

int main()
{
	Mat src, sobel_1;
	Mat grad, prewitt;

	src = imread("dog.png", IMREAD_GRAYSCALE);
	if (src.empty()) { return -1; }

	imshow("Image", src);
	maded_sobel(src, sobel_1, 100);
	imshow("sobel", sobel_1);
	maded_prewitt(src, prewitt, 100);
	imshow("prewitt", prewitt);

	waitKey(0);
	return 0;


}

왼쪽이 소벨 오른쪽이 프리윗 마스크가 적용된 이미지입니다.

 

 

#include <opencv2/opencv.hpp>
#include <opencv2/highgui.hpp>
#include <iostream>
using namespace cv;
using namespace std;

Mat image, roi;
int mx1, my1, mx2, my2;
bool cropping = false;

void onMouse(int event, int x, int y, int flags, void* param)
{
	if(event == EVENT_LBUTTONDOWN) {
    	mx1 = x;
        my1 = y;
        cropping = true;
    }
    else if(event == EVENT_LBUTTONUP)
    {
    	mx2 = x;
        my2 = y;
        cropping = false;
        rectangle(image, Rect(mx1, my1, mx2 - mx1, my2 - my1), Scalar(0, 255, 0), 2);
        imshow("image", image);
    }
}

int main()
{
	image = imread("dog.png", IMREAD_COLOR);
    Mat clone = image.clone();
    
    if(image.empty())
    	cout << "영상을 읽을 수 없음" << endl;
    imshow("image", image);
    setMouseCallback("image", onMouse);
        
        while(1) {
        	int key = waitKey(100);
            
            if(key == 'q') break;
            else if(key == 'c') {
            roi = clone(Rect(mx1, my1, mx2 - mx1, my2 - my1));
            imshow("press_c", roi);
            }
            else if(key == 's'){
            	resize(image, image, Size(128, 128));
                imshow("image", image);
            }
            eles if(key == 'g') {
            	cvtColor(image, image, COLOR_BGR2GRAY);
                imshow("image", image);
            }
        }
        
        return 0;
 }

 

실행시켰을 경우

 

드래그 하였을 경우

c 클릭시 드래그 영역이 새로운 윈도우 창으로 나타난다.

g를 눌렀을 경우 색상이 컬러에서 흑백으로 바뀐다.

s를 눌렀을 경우 사이즈가 아까 드래그한 사각형의 창보다 작아진 것을 확인할 수 있다. 현재 위의 윈도우 크기는 128*128 이다.

 

 

1. 영상처리, 컴퓨터비전, 컴퓨터그래픽스의 관계에 대하여 설명하세요

영상 처리는 입력 영상을 처리하여 출력 영상을 얻는 기술이다.

컴퓨터 비전은 영상 처리된 영상을 처리하여 정보를 얻어내는 기술이다.

컴퓨터그래픽스 정보를 처리하여 이미지로 만드는 것이다.

 

샘플링과 영자화에 대하여 설명하세요

샘플링은 수많은 데이터로부터 유한한 개수의 데이터를 뽑아내는 것이다.

양자화는 샘플링한 아날로그 형태로되어 있는 신호나 정보를 디지털화하는 작업을 말한다.

 

컴퓨터 비전 응용분야

문자 인식, 생체 인식,  스마트 팩토리 불량 검사, 자율 주행 자동차

 

 

 

 

노이즈 만들기

Mat noise_img = Mat::zeros(src.rows, src.cols, CV_8U);
randu(noise_img, 0, 255);

Mat black_img = noise_img < 10; 
Mat white_img = noise_img > 245; 

Mat src1 = src.clone();
src1.seTo(225, white_img)
src1.seTo(0, black_img);
medianBlur(src1, dst, 5);

at()함수

#include "opencv2/opencv.hpp"
using namespace cv;
using namespace std;

int main()
{
	Mat img = imread("d:/lenna.jpg", IMREAD_GRAYSCALE);
    imshow("Original Image", img);
	
    for (int r = 0; r < img.rows; r++)
    	for(int c = 0; c < img.cols; ++c)
        	img.at<uchar>(r, c) = img.at<uchar>(r, c) + 30;
            
        imshow("New Image", img);
        waitKey(0);
        return 0;
}

위 코드는 오류가 발생한다.

화소의 값이 30이 더해지면 255를 넘게 되어서 오버플로우가 일어난다.

 

방지를 위해 img.at<uchar>(r, c) = saturate_cast<uchar>(img.at<uchar>(r, c) + 30);

saturate_cast<uchar> 을 사용한다.

 

영상의 밝기를 증가시키는 함수 convertTo()

#include "opencv2/opencv.hpp"
using namespace cv;
using namespace std;

int main()
{
	Mat img = imread("d:/lenna.jpg", IMREAD_GRAYSCALE);
    imshow("Original Image", img);
    
    Mat oimage;
    img.convertTo(oimage, -1, 1, 30);
    imshow("New Image", oimage);
    waitKey(0);
    return 0;
}

 

 

이진화

threshold(image, dst, threshold_value, 255, THRESH_BINARY);

 

반전 영상 만들기 LUT(Look Up Table)

Mat table(1, 256, CV_8U);

LUT(img1, table, img2);

 

영상 합성

dst = src1 + src2;

 두 영상을 더하면 된다.

 

선형영상합성

cout << "알파값을 입력하시오;
cin >> input;
beta = (1.0-alpha);
addWeight(src1, alpha, src2, beta, 0.0, dst);

 

논리적영상합성

bitwise_and(img1, mask, dst);

 

히스토그램 계산하기

int main()
{
	Mat src = imread("d:/lenna.jpg", IMREAD_GRAYSCALE);
    imshow("Input Image", src);
    int histogram[256] = {0};
    
    for (int y = 0; y < src.rows; y++)
    	for(int x = 0, x < src.cols; x++)
        	histogram[(int)src.at<uchar>(y,x)]++;
            
        for (int count : histogram)
        	cout << count << ",";
        waitKey(0);
        return 0;
}

 

 

히스토그램 그리기

// 히스토그램을 받아서 막대그래프로 그린다.
void drawHist(int histogram[])
{
	int hist_w = 512;  // 히스토그램 영상의 폭
    int hist_h = 400;  // 히스토그램 영상의 높이
    int bin_w = cvRound((double)hist_w / 256);  // 빈의 폭
    
    // 히스토그램이 그려지는 영상(컬러로 정의)선언
    Mat histImage(hist_h, hist_w, CV_8UC3, Scalar(255, 255, 255));
    
    // 히스토그램에서 최대값을 찾는다.
    int max = histogram[0];
    for (int i = 1; i < 256; i++) {
    	if(max < histogram[i])
        	max = histogram[i];
     }
     // 히스토그램 배열을 최대값으로 정규화한다.(최대값이 최대 높이가 되도록).
     for (int i = 0; i < 255; i++) {
     	histogram[i] = floor(((double)histogram[i] / max)*histImage.rows);
     }
     // 히스토그램의 값을 빨강색 막대로 그린다.
     for (int i=0; i<255; i++) {
     	line(histImage, Point(bin_w*(i), hist_h),
        	Point(bin_w*(i), hist_h - histogram[i]), Scalar(0, 0, 255));
     }
     imshow("Histogram", histImage);
            
 }
 
 int main()
 {
 	Mat src = imread("lenna.jpg", IMREAD_GRAYSCALE);
    imshow("Input Image", src);
    int histogram[256] = {0};
    
    for (int y = 0; y <src.rows; y++)
    	for(int x=0; x<src.cols; x++)
        	histogram[(int)src.at<uchar>(y, x)]++;
            
    drawHist(histogram);
    waitKey(0);
    return 0;
 }

 

히스토그램 그리기(컬러)

calcHist(&bgr_planes[0], 1, 0, Mat(), b_hist, 1, &histSize, &histRange, uniform, accumulate);
calcHist(&bgr_planes[1], 1, 0, Mat(), g_hist, 1, &histSize, &histRange, uniform, accumulate);
calcHist(&bgr_planes[2], 1, 0, Mat(), r_hist, 1, &histSize, &histRange, uniform, accumulate);

 

히스토그램 스트레칭

int stretch(int x, int r1, int s1, int r2, int s2)
{
	float result;
    if (0 <= x && x <= r1) {
    	result = s1 / r1 * x;
     }
     else if (r1 < x && x <= r2) {
     	result = ((s2 - s1) / (r2 - r1)) * (x - r1) + s1;
     }
     else if (r2 < x && x <= 255) {
     	result = ((255 - s2) / (255 - r2)) * (x - r2) + s2;
     }
     return (int)result;
 }
 
 int main()
 {
 	Mat image = imrad("d:/lenna.jpg");
    Mat new_image = image.clone();
    
    int r1, s1, r2, s2;
    cout << "r1를 입력하시오: "; cin >> r1;
    cout << "r2를 입력하시오: "; cin >> r2;
    cout << "s1를 입력하시오: "; cin >> s1;
    cout << "s2를 입력하시오: "; cin >> s2;
    
    for (int y = 0; y < image.rows; y++) {
    	for (int x = 0; x < image.cols; x++) {
        	for (int c = 0; c < 3; c++) {
            	int output = stretch(image.at<Vec3b>(y, x)[c], r1, s1, r2, s2);
                new_image.at<Vec3b>(y, x)[c] = saturate_cast<uchar>(output);
                }
            }
        }
        ... //결과 영상 출력
  }

 

히스토그램 평활화

int main()
{
	Mat src = imread("d:/crayfish.jpg", IMREAD_GRAYSCALE);
    if (src.empty()) { return -1;}
    
    Mat dst;
    equalizeHist(src, dst);
    
    imshow("Image", src);
    imshow("Equalized", dst);
    waitKey(0);
    return 0;
}

 

전경과 배경 분리

using namespace std;
using namespace cv;

int main()
{
	Mat src, dst;
	src = imrad("d:/plane.jpg", IMREAED_GRAYSCALE);
    imshow("Image", src);
    if(!src.data) { return -1;}
    
    Mat threshold_image;
    threshold(src, threshold_image, 100, 255, THRESH_BINARY);
    imshow("Thresholded", threshold_image);
    waitkey(0);
    return 0;
}

 

향상된 이진화 방법

threshold(src, threshold_image, 0, 255, CV_THRESH_BINARY | CV_THRESH_OTSU);

 

평균값 필터링의 구현

Mat mask(3, 3, CV_32F, weights);
Mat blur;
filter2D(image, blur, -1, mask);
blur.convertTo(blur, CV_8U);

 

평균값 필터링

blur(src, dst, Size(11, 11));

 

가우시안 필터링

#include <opencv2/opencv.hpp>
using namespace cv;

int main()
{
	Mat src = imraed("d:/lenna.jpg", 1);
    Mat dst;
    imshow("src", src);
    
    for (int i = 1; i<61; i = i + 2)
    {
    	GaussianBlur(src,dst,Size(i, i), 0, 0);
        imshow("Gaussian filter", dst);
        waitKey(1000);
     }
}

 

 

샤프닝

float weights1[9] = { -1, -1, -1, -1, 5, -1, -1, -1, -1};

float weights2[9] = { -1, -1, -1, -1, 9, -1, -1, -1, -1};



Mat mask1 = Mat(3, 3, CV_32F, weights1);

Mat mask2 = Mat(3, 3, CV_32F, weights2);



filter2D(src, dst1, -1, mask, Point(-1, -1), 0, BORDER_DEFAULT);

filter2D(src, dst2, -1, mask, Point(-1, -1), 0, BORDER_DEFAULT);

 

 

소벨 

int main()
{
    Mat src;
    Mat grad;
    int scale = 1;
    int delta = 0;
    src = imread("d:/lenna.jpg", IMREAD_GRAYSCALE);
    if (src.empty()) { return -1; }
    Mat grad_x, grad_y;
    Mat abs_grad_x, abs_grad_y;
    Sobel(src, grad_x, CV_16S, 1, 0, 3, scale, delta, BORDER_DEFAULT);
    Sobel(src, grad_y, CV_16S, 0, 1, 3, scale, delta, BORDER_DEFAULT);
    convertScaleAbs(grad_x, abs_grad_x);
    convertScaleAbs(grad_y, abs_grad_y);
    addWeighted(abs_grad_x, 0.5, abs_grad_y, 0.5, 0, grad);
    imshow("Image", src);
    imshow("Sobel", grad);
    waitKey(0);
    return 0;
 }

 

라플라시안

GaussianBlur(src, src, SIZE(3, 3), 0, 0, BORDER_DEFULAT);
Mat abs_dst;
Laplacian(src, dst, ddepth, kernel_size, scale, delta, BORDER_DEFAULT);
convertScaleAbs(dst, abs_dst);

 

 

 

<화소처리>

화소의 값이 오버플로우 되지 않기 위해 saturate_cast<uchar>() 을 사용한다.

ex) saturate_cast<uchar>(img.at<uchar>(r.c) + 30);

 

영상의 밝기를 증가시키는 convertTo() 함수

ex) img.convert(oimage, -1, 1, 30);    1은 알파로 곱하기, 30은 베타로 더하기를 나타낸다.

 

이진화

double threshold(InputArray src, OutputArray dst, double thresh, double maxval, int type)

매개 변수 설명 
src 입력 영상. (1채널이어야 한다)
dst 출력 영상
thresh 임계값(이 값을 기준으로 이진화 된다.)
maxval 가능한 최대 출력값
type 이진화 종류, 보통 THESH_BINARY

 

선형영상합성

ex) addWeighted(src1, alpha, src2, beta, 0,0, dst);

 

논리적인 영상 합성

ex) bitwise_and(평범한 사진, 검정색 배경에 하얀색 도형이 그려져 있는 사진, dst)

앤드 연산으로 인해 하얀색 도형의 부분만 평범한 사진의 일부를 나타낸다.

 

<히스토그램>

히스토그램

- 관측 값의 개수를 겹치지 않는 다양한 계급으로 표시하는 것

장점: 화소값들의 분포를 한눈에 볼 수 있다.

 

히스토그램 스트래징

- 히스토그램의 분포가 좁아서 영상의 대비가 좋지 않은 영상의 화질을 개선할 수 있는 알고리즘

 

 

히스토그램 평활화

- 화소값의 분포를 나타내는 히스토그램이 균일하게 되도록 변환하는 처리이다.

ex) equalizeHist(src,dst)

 

threshold에서 CV_THRESH_BINARY | CV_THRESH_OTSU

 

<공간 필터링>

 

공간 필터링

- 인접 화소들의 값을 참조하여 화소의 값을 변경하는 처리

 

컨벌루션

- 중심 화소의 값을 인접 화소값들의 가중 합으로 대체하는 연산이다.

 

자신에게 필요하여 선택된 정보를 전경, 배후에 있는 제외된 정보를 배경이라고 부른다.

 

미분 - 함수의 순간 변화율 

 

평균값 필터링

ex) blur(src, dst, Size(11, 11));

 

가우시안 필터링

ex) GaussianBlur(src, dst, Size(i, i), 0, 0);

 

샤프닝(sharpening)

- 출력화소에서 이웃 화소끼리 차이를 크게 해서 날카로운 느낌이 나게 만드는 것

- 영상의 세세한 부분을 강조할 수 있으며, 경계 부분에서 명암대비가 증가되는 효과

- 블러링은 부드러운 영상을 만듦. 명함대기가 감소

샤프닝 마스크

- 마스크 원소들의 값 차이가 커지도록 구성

- 마스크 원소 전체합이 1이 되어야 입력영상 밝기가 손실 없이 출력영상 밝기로 유지

 

에지 검출

에지는 영상에서 화소의 발기가 급격하게 변하는 부분이다.

 

밝기 변화율: 에지의 강도

기울기: 에지의 방향

 

그래디언트(gradient, 변화율)

- 그래디언트 방향은 경계선에 수직

 

1차 미분을 이용한 에지 검출

Roberts, Prewitt, Sobel

 

 

샤르 마스크(Scharr Mask)

소벨 연산자의 단점: 작은 크기의 커널을 사용할 때 정확도가 떨어짐. 큰 커널인 경우는 어느 정도 극복

3x3짜리 필터를 사용해서 영상측정값을 구할 땐, 항상 사용해야

 

2차 미분 마스크

라플라시안 에지 검출

 

LoG(Laplacian of Gaussian)

- 라플라시안은 노이즈에 민감한 단점

- 따라서, 먼저 노이즈를 제거 후 라플라시안을 수행 -> 노이즈에 강한 에지 검출이 가능

 

DoG(Difference of Gaussian)

-단순한 방법으로 2차 미분 계산

-가우시안 스무딩 필터링의 차이를 이용해서 에지를 검출하는 방법

 

주성분 분석

 

캐니 에지 검출

1. 블러링을 통한 노이즈 제거 (가우시안 필터링)

2. 화소 기울기(gradiant)의 강도와 방향 검출(소벨 마스크)

3. 비최대치 억제(non-maximum suppression)

4. 이력 임계값(hysteresis threshold)으로 에지 결정

 

 

Canny(src_image, detected_edges, lowThreshold, highThreshold, kernel_size);

 

입력 영상

출력 영상

하위 임계값

상위 임계값

3(내부적으로 사용되는 Sobel 마스크의 크기)로 정의한다.

 

 

<기하학적 변환>

 

기하학적 변환(geometric trasfortation)은 영상을 이동하거나 영상의 모양을 변형하는 처리

영상의 정확한 인식을 위해 기하학적 변환이 필요하다

 

역방향 사상

목적영상의 좌표를 중심으로 역변환을 계산하여, 그 좌표에 해당하는 입력 영상의 좌표를 찾아서 화소값을 가져오는 방식

 

역방향 사상 -> 영상을 축소할 때에 오버랩의 문제가 발생한다.

 

해결책: 보간법이 필요

목적영상에서 홀의 화소들을 채우고, 오버랩이 되지 않게 화소들을 배치하여 목적영상을 만드는 기법

 

최근접 이웃 보간법

변화된 위치와 가장 가까운 화소값을 사용하는 방법

 

선형 보간 사용하는 이유

최근접 이웃 보간법은 확대비율이 커지면, 모자이크 현상 혹은 경계부분에서 계단현상 발생

직선의 선상에 위치한 중간 화소들의 값은 직선의 수식을 이용해서 쉽게 계산

 

양선형 보간법 - 선형 보간을 두 번에 걸쳐서 수행하기에 붙여진 이름

 

warpAffine (src, dst, M, dsize, flags = INTER_LINEAR)

 

M: 어파인변환 행렬

INTER_NEAREST = 0  // 최근접 보간법

INTER_LINEAR = 1     // 양선형 보간법

INTER_CUBIC = 2     // 3차 보간법

 

int main()
{
	Mat src = imread("d:/lenna.jpg", IMREAD_COLOR);
	Mat dst = Mat();
	Size dsize = Size(src.cols, src.rows);
    Point center = Point(src.cols / 2.0, src.rows / 2.0);
    Mat M = getRotationMatrix2D(center, 45, 1.0);
    warpAffine(src, dst, M, dsize, INTER_LINEAR);
    imshow("Image", src);
    imshow("Rotated", dst);
    waitKey(0);
    return 1;
}

레나라는 사진이 45도 회전하게 된다.

 

원근감(Depth Feeling)

동일한 크기의 물체라도 시점으로부터 멀리 있는 것은 작게 보이고 가까운 것은 크게 보임

 

소실점(VP: Vanishing Point)

원근투상 결과 평행선이 만나는 점(시점 높이)

 

warp: 경사

 

 

warpPerspective(src, dst, M, dsize)

입력 영상
출력 영상

3x3 변환 행렬

dsize 출력 영상의 크기

 

int main()
{
	Mat src = imread("d:/book.jpg");
    
	Point2f inputp[4];
	inputp[0] = Point2f(30, 81);
	inputp[1] = Point2f(274, 247);
	inputp[2] = Point2f(298, 40);
	inputp[3] = Point2f(598, 138);
	Point2f outputp[4];
	outputp[0] = Point2f(0, 0);
	outputp[1] = Point2f(0, src.rows);
	outputp[2] = Point2f(src.cols, 0);
	outputp[3] = Point2f(src.cols, src.rows);
    
	Mat h = getPerspectiveTransform(inputp, outputp);
	Mat out;
	warpPerspective(src, out, h, src.size());
	imshow("Source Image", src);
	imshow("Warped Source Image", out);
	waitKey(0);
}

 

결과

<모폴로지> - 형태학

- 영상의 객체들의 형태(shape)를 분석하고 처리하는 기법

- 영상의 경계, 골격, 블록 등의 형태를 표현하는데 필요한 요소 추출함

- 영상 내에 존재하는 객체의 형태를 조금씩 변형시킴으로써 영상 내에서 불필요한 노이즈 제거하거나 객체를 뚜렷하게    함

 

 

침식 연산

- 객체를 침식시키는 연산

- 객체의 크기를 축소, 배경을 확장

- 영상 내에 존재하는 노이즈 같은 작은 크기의 객체 제거가 가능

- 소금-후추 노이즈와 같은 임펄스 노이즈를 제거

 

erode: 침식하다

int main()
{
	Mat src, dst, erosion_dst, dilation_dst;
    src = imread("d:/test.png", IMREAD_GRAYSCALE);
    
    threshold(src, dst, 127, 255, THRESH_BINARY);
    imshow("dst", dst);
    
    Mat element = getStructuringElement(MORPH_RECT,
    Size(3, 3),
    Point(-1, -1));
    
    erode(dst, erosion_dst, element);
    imshow("Erosion Demo", erosion_dst);
    waitKey(0);
    return 0;
}

결과를 통해 침식이 더 심해진 것을 확인할 수 있다.

팽창 연산

객체를 팽창시키는 연산

- 객체의 최외곽 화소를 확장시키는 기능 -> 객체 크기가 확대, 배경이 축소

- 객체 팽창으로 객체 내부의 빈 공간도 메워짐

    -> 객체 내부 노이즈 제거

 

열림 연산

침식 연산 -> 팽창 연산

 

int main() 
{
    Mat input_image = (Mat_<uchar>(8, 8) <<
    0, 0, 0, 0, 0, 255, 0, 0,
    0, 255, 255, 255, 0, 0, 0, 0,
    0, 255, 255, 255, 0, 0, 255, 255,
    0, 255, 255, 255, 0, 255, 255, 255,
    255, 255, 255, 0, 0, 255, 255, 255,
    0,0, 0, 0, 255, 255, 255, 255,
    0, 0, 0, 0, 255, 255, 255, 0, 
    0, 0, 0, 0, 0, 0, 0, 0 );
    Mat kernel = (Mat_<int>(3, 3) <<
    1, 1, 1, 1, 1, 1, 1, 1, 1);
    Mat output_image;
    morphologyEx(input_image, output_image, MORPH_OPEN, kernel);
    const int rate = 50;
    resize(input_image, input_image, Size(), rate, rate, INTER_NEAREST);
    imshow("Original", input_image);
    resize(output_image, output_image, Size(), rate, rate, INTER_NEAREST);
    imshow("Open", output_image);
    waitKey(0);
    return 0;
}

닫힘 연산

- 팽창 연산 -> 침식 연산

- 팽창 연산으로 객체가 확장되어서 객체 내부의 빈 공간이 메워짐

- 침식 연산으로 다음으로 확장되었던 객체의 크기가 원래대로 축소

 

 

형태학적인 그라디언트

외각선 추출

외곽선을 추출하려면 먼저 영상에 침식 연산을 적용한다. 앞에서 설명한 바와 같이 영상 내의 물체는 한 화소 축소된다.

그 다음에 원 영상에서 침식 영상을 뺀다. 그 결과는 물체의 외곽선만을 보여주는 영상이 된다.

 

int main()
{
    Mat src, dst, open, close;
    src = imread("d:/letterj.png", IMREAD_GRAYSCALE);
    imshow("src", src);
    Mat element = getStructuringElement(MORPH_RECT, Size(5, 5));
    morphologyEx(src, open, MORPH_GRADIENT, element);
    imshow("Gradient Demo", open);
    waitKey(0);
    return 0;
}

골격화

골격화는 골격선을 구하는 연산

간단하지만 결과가 좋지 않은 경우가 다수 있다.

 

<컬러 영상 처리>

 

BGR 영상에서 B, G, R을 분리하여 윈도우에 표시하려면 -> split() 함수를 사용한다.

 

int main()
{
	Mat image;
    image = imread("d:/rcube.jpg", CV_LOAD_IMAGE_COLOR);
    
    Mat bgr[3];
    split(image, bgr);
    
    imshow("src", image);
    imshow("blue", bgr[0]);
    imshow("green", bgr[1]);
    imshow("red", bgr[2]);
    waitKey(0);
    return 0;
}

 

cvtColor(src, dst, code);

src : 입력영상

dst: 출력 영상

code: 색상 공간 변환 코드이다.

COLOR_BGR2HSV: BGR 컬러 모델을 HSV 컬러 모델로 변경한다.

 

#include "opencv2/opencv.hpp"
#include <iostream>
using namespace cv;
using namespace std;

int main()
{
	Mat img = imread("d:/lenna.jpg");
	Mat img_color;
	applyColorMap(img, img_color, COLORMAP_HOT);
	imshow("img_color", img_color);
	waitKey(0);
}

컬러를 통한 객체 분할

int main()
{
    Mat img = imread("d:/image1.jpg", IMREAD_COLOR);
    if (img.empty()) { return -1; }
    Mat imgHSV;
    cvtColor(img, imgHSV, COLOR_BGR2HSV);
    Mat imgThresholded;
    inRange(imgHSV, Scalar(100, 0, 0),
    Scalar(120, 255, 255), imgThresholded);
    imshow("Thresholded Image", imgThresholded);
    imshow("Original", img);
    waitKey(0);
    return 0;
}

 

크로마키 기법

크로마키 합성(Chroma Key Composing)은 색조를 이용하여 2개의 영상 또는 비디오 스트림을 합성하는 기술이다.

 

int main()
{
    Mat img = imread("d:/chroma.jpg", IMREAD_COLOR);
    Mat img2 = imread("d:/beach.png", IMREAD_COLOR);
    Mat converted;
    cvtColor(img, converted, COLOR_BGR2HSV);
    Mat greenScreen = converted.clone();
    inRange(converted, Scalar(60-10, 100, 100), Scalar(60+10, 255, 255), 
    greenScreen);
    Mat dst, dst1, inverted;
    bitwise_not(greenScreen, inverted);
    bitwise_and(img, img, dst, inverted);
    bitwise_or(dst, img2, dst1, greenScreen);
    bitwise_or(dst, dst1, dst1);
    imshow("img", img);
    imshow("green", greenScreen);
    imshow("dst", dst);
    imshow("dst1", dst1);
    waitKey(0);
    return 0;
}

 

<주파수 영역 처리>

헤르츠(Hz) 

주파수를 표현하는 단위, 1초 동안에 진동하는 횟수

 

영상처리에서는 공간 주파수(spatial frequency)개념 사용

확장된 의미에서 주파수

이벤트가 주기적으로 재발생하는 빈도

 

공간 주파수 - 밝기의 변화 정도에 따라서 고주파 영역 / 저주파 영역으로 분류

 저주파 공간 영역

 - 화소 밝기가 거의 변화가 없거나 점진적으로 변화하는 것

 - 영상에서 배경 부분이나 객체의 내부에 많이 있음

 

고주파 공간 영역

- 화소 밝기가 급변하는 것

- 경계부분이나 객체의 모서리

 

고주파 성분 제거한 영상 -> 경계 흐려진 영상

고주파 성분만 취한 영상 -> 경계나 모서리만 포함하는 영상 즉, 에지 영상

 

푸리에 변환

- 시간에 따라 변화하는 함수를 분해하여 그 안에 들어있는 주파수 성분을 추출하는 변환

 

#include <opencv2/imgproc.hpp>

#include <opencv2/highgui.hpp>

#include <opencv2/imgproc/imgproc.hpp>

#include <iostream>

#include<time.h>

#include<conio.h>

 

using namespace cv;

using namespace std;

 

 

void put_string(Mat& frame, string text, Point pt)

{

    Point shade = pt + Point(2, 2);

    int font = FONT_HERSHEY_SIMPLEX;

    putText(frame, text, shade,font, 0.7, Scalar(0, 0, 0), 2);        // 그림자 효과

    putText(frame, text, pt, font, 0.7, Scalar(120, 200, 90), 2);   // 작성 문자

}

 

void put_circle(Mat& frame)

{

    int font = FONT_HERSHEY_SIMPLEX;

    circle(frame, Point(15, 240), 7, Scalar(0, 0, 255),-1);

}

 

int main(int argc, char** argv)

{

    

    VideoCapture capture;

    capture.open("video.mp4"); // 동영상 파일 개방

    CV_Assert(capture.isOpened());

    double frame_rate = capture.get(CAP_PROP_FPS); // 초당 프레임 수

    int delay = 1000 / frame_rate;        // 지연시간

    int frame_cnt = 0;

    Mat frame;

    time_t now;

    bool save_on = false;

    bool capture_on = false;

    bool stop_on = false;

    int key;

    int i = 0;

 

 

    Size size = Size((int)capture.get(CAP_PROP_FRAME_WIDTH), (int)capture.get(CAP_PROP_FRAME_HEIGHT));

    int fourcc = VideoWriter::fourcc('D', 'X', '5', '0');   // 압축 코덱 설정

 

    capture.set(CAP_PROP_FRAME_WIDTH, size.width);

    capture.set(CAP_PROP_FRAME_HEIGHT, size.height);

 

    VideoWriter writer; // 동영상 파일 저장 객체

    writer.open("video_file.avi", fourcc, CAP_PROP_FPS, size);

    CV_Assert(writer.isOpened());

 

 

 

 

    while (capture.read(frame)) // 프레임 반복 재생

    {

        now = time(NULL);

 

        key=waitKey(delay);        // 프레임간 지연시간 지정

 

        if (key == 'e')

        {

            save_on = true;

            stop_on = false;

            capture_on = false;

        }

        else if (key == 's')

        {

            save_on = false;

            stop_on = true;

            capture_on = false;

        }

        else if (key == 'c')

        {

            i++;

            string str = "capture" + to_string(i) + ".jpg";

            imwrite(str, frame);

            save_on = false;

            stop_on = false;

            capture_on = true;

 

        }

        else if (key == 27) break;

 

        if (frame_cnt < 100);

        else if ((frame_cnt < 200)) frame -= Scalar(0, 0, 100);

        else if ((frame_cnt < 300)) frame += Scalar(100, 0, 0);

        else if ((frame_cnt < 400)) frame = frame * 1.5;

        else if ((frame_cnt < 500)) frame = frame * 0.5;

 

 

 

 

        if (save_on)

        {

            writer << frame;

            put_string(frame, "SaveVideo", Point(10, 20));

        }

        else if(stop_on)

            put_string(frame, "StopSave", Point(10, 20));

        else if(capture_on)

            put_string(frame, "CaptureVideo", Point(10, 20));

 

        if (save_on && (int)now % 2 == 0)

        {

            put_circle(frame);

        }

 

        imshow("동영상 파일읽기", frame);

 

    }

 

    //waitKey(0);

}

 

+ Recent posts