ApplyColorMap for pseudocoloring

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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
// ApplyColorMap for pseudocoloring
// using OpenCV 3.0 Version
 
#include <cv.hpp>
 
using namespace cv;
using namespace std;
 
string colormap_name(int id)
{
    switch (id){
    case COLORMAP_AUTUMN:
        return "COLORMAP_AUTUMN";
    case COLORMAP_BONE:
        return "COLORMAP_BONE";
    case COLORMAP_JET:
        return "COLORMAP_JET";
    case COLORMAP_WINTER:
        return "COLORMAP_WINTER";
    case COLORMAP_RAINBOW:
        return "COLORMAP_RAINBOW";
    case COLORMAP_OCEAN:
        return "COLORMAP_OCEAN";
    case COLORMAP_SUMMER:
        return "COLORMAP_SUMMER";
    case COLORMAP_SPRING:
        return "COLORMAP_SPRING";
    case COLORMAP_COOL:
        return "COLORMAP_COOL";
    case COLORMAP_HSV:
        return "COLORMAP_HSV";
    case COLORMAP_PINK:
        return "COLORMAP_PINK";
    case COLORMAP_HOT:
        return "COLORMAP_HOT";
    }
 
    return "NONE";
}
 
int main()
{
    Mat im = imread("img/pluto.jpg", IMREAD_GRAYSCALE);
 
    Mat im_out = Mat::zeros(600800, CV_8UC3);
 
    for (int i = 0; i < 4; i++){
        for (int j = 0; j < 3; j++){
            int k = i + j * 4;
            Mat im_color = im_out(Rect(i * 200, j * 200200200));
            applyColorMap(im, im_color, k);
            putText(im_color, colormap_name(k), Point(30180), 
                CV_FONT_HERSHEY_DUPLEX, 0.5, Scalar::all(255), 1, CV_AA);
        }
    }
 
    imshow("Pseudo Colored", im_out);
    imwrite("img/Pseudo COlored.jpg", im_out);
    waitKey(0);
    
    return 0;
}
cs





Non-Photorealistic Rendering

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
30
31
32
// Non-Photorealistic Rendering using OpenCV
// using OpenCV 3.0 Version
 
#include <cv.hpp>
 
using namespace std;
using namespace cv;
 
int main()
{
    Mat im = imread("img/cow.jpg");
    Mat imout, imout_gray;
 
    // Edge preserving filter with two different flags;
    edgePreservingFilter(im, imout, RECURS_FILTER);
    imwrite("img/edge-preserving-recursive-filter.jpg", imout);
 
    edgePreservingFilter(im, imout, NORMCONV_FILTER);
    imwrite("img/edge-preserving-normlized-convolution-filter.jpg", imout);
 
    // Detail enhance filter
    detailEnhance(im, imout);
    imwrite("img/detail-enhance.jpg", imout);
 
    // Pencil sketch filter
    pencilSketch(im, imout_gray, imout);
    imwrite("img/pencil-sketch.jpg", imout_gray);
 
    // stylization filter
    stylization(im, imout);
    imwrite("img/stylization.jpg", imout);
}
cs


Domain Transform for Edge-aware Filtering












OpenCV SeamlessCloning

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
30
31
32
33
34
// Examples : seamlessClone.cpp
// Using OpenCV 3.0 version
 
#include <cv.hpp>
 
using namespace std;
using namespace cv;
 
int main()
{
    Mat src = imread("img/iloveyouticket.jpg");
    Mat dst = imread("img/wood-texture.jpg");
 
    // create an all white mask
    Mat src_mask = 255 * Mat::ones(src.rows, src.cols, src.depth());
 
    // The location of the center of the src in the dst
    Point center(dst.cols / 2, dst.rows / 2 );
 
    // Seamlessly Clone src into dst and put the result in output
    Mat normal_clone, mixed_clone;
 
    seamlessClone(src, dst, src_mask, center, normal_clone, NORMAL_CLONE);
    seamlessClone(src, dst, src_mask, center, mixed_clone, MIXED_CLONE);
 
    namedWindow("normal_clone");
    imshow("normal_clone", normal_clone);
    namedWindow("mixed_clone");
    imshow("mixed_clone", mixed_clone);
 
    waitKey(0);
 
    return 0;
}
cs





입력 이미지 예1)


  <그림 1 source image>


                  <그림 2 dst image>


                <그림 3 normal clone


               <그림 4 mixed clone




입력 이미지 예2)

         <그림 5 source image


                          <그림 6 dst image


normal clone

                         <그림 7 normal clone


mixed clone

                          <그림 8 mixed clone

High Dynamic Range Imaging(HDR)

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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
// Reference : http://docs.opencv.org/3.0.0/d3/db7/tutorial_hdr_imaging.html
// Examples : High Dynamic Range Imaging(HDR)
 
#include <cv.hpp>
#include <iostream>
#include <fstream>
 
using namespace std;
using namespace cv;
 
void loadExposureSqe(String path, vector<Mat>& images, vector<float>&times)
{
    path = path + std::string("/");
    ifstream list_file((path + "list.txt").c_str());
 
    string name;
    float val;
 
    while (list_file >> name >> val) {
        Mat img = imread(path + name);
        images.push_back(img);
        times.push_back(1 / val);
    }
 
    list_file.close();
}
 
int main()
{
    vector<Mat> images;
    vector<float> times;
    loadExposureSqe("hdr/exposures", images, times);
 
    Mat response;
    Ptr<CalibrateDebevec>  calibrate = createCalibrateDebevec();
    calibrate->process(images, response, times);
 
    Mat hdr;
    Ptr<MergeDebevec> merge_debevec = createMergeDebevec();
    merge_debevec->process(images, hdr, times, response);
 
    Mat ldr;
    Ptr<TonemapDurand> tonemap = createTonemapDurand(2.2f);
    tonemap->process(hdr, ldr);
 
    Mat fusion;
    Ptr<MergeMertens> merge_mertens = createMergeMertens();
    merge_mertens->process(images, fusion);
 
    imwrite("hdr/fusion.png", fusion * 255);
    imwrite("hdr/ldr.png", ldr * 255);
    imwrite("hdr/hdr.hdr", hdr);
    
    return 0;
}
cs






 

    hdr.zip





OpenCV Principal Component Analysis(PCA)

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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
#include <cv.hpp>
#include <iostream>
using namespace std;
using namespace cv;
 
void drawAxis(Mat &img, Point p, Point q, Scalar colour, const float scale = 0.2)
{
    double angle;
    double hypotenuse;
    angle = atan2((double)p.y - q.y, (double)p.x - q.x);
    hypotenuse = sqrt((double)(p.y - q.y) * (p.y - q.y) + (p.x - q.x) * (p.x - q.x));
 
    // Here we lengthen the arrow by a factor of scale
    q.x = (int)(p.x - scale * hypotenuse * cos(angle));
    q.y = (int)(p.y - scale * hypotenuse * sin(angle));
    line(img, p, q, colour, 1, CV_AA);
 
    // create the arrow hooks
    p.x = (int)(q.x + 9 * cos(angle + CV_PI / 4));
    p.y = (int)(q.y + 9 * sin(angle + CV_PI / 4));
    line(img, p, q, colour, 1, CV_AA);
    
    p.x = (int)(q.x + 9 * cos(angle - CV_PI / 4));
    p.y = (int)(q.y + 9 * sin(angle - CV_PI / 4));
    line(img, p, q, colour, 1, CV_AA);
}
 
double getOrientation(const vector<Point> &pts, Mat &img)
{
    // construct a buffer used by the pca analysis
    int sz = static_cast<int>(pts.size());
    Mat data_pts = Mat(sz, 2, CV_64FC1);
 
    for (int i = 0; i < data_pts.rows; ++i)
    {
        data_pts.at<double>(i, 0= pts[i].x;
        data_pts.at<double>(i, 1= pts[i].y;
    }
 
    //Perform PCA analysis
    PCA pca_analysis(data_pts, Mat(), CV_PCA_DATA_AS_ROW);
 
    //Store the center of the object
    Point cntr = Point(static_cast<int>(pca_analysis.mean.at<double>(00)), 
        static_cast<int>(pca_analysis.mean.at<double>(01)));
 
    //Store and eigenvalues and eigenvectors
    vector<Point2d> eigen_vecs(2);
    vector<double> eigen_val(2);
 
    for (int i = 0; i < 2++i)
    {
        eigen_vecs[i] = Point2d(pca_analysis.eigenvectors.at<double>(i, 0),
            pca_analysis.eigenvectors.at<double>(i, 1));
 
        eigen_val[i] = pca_analysis.eigenvalues.at<double>(i, 0);
    }
 
    //Draw the principal components
    circle(img, cntr, 3, Scalar(2550255), 2);
    Point p1 = cntr + 0.02 * Point(static_cast<int>(eigen_vecs[0].x * eigen_val[0]), 
        static_cast<int>(eigen_vecs[0].y * eigen_val[0]));
    Point p2 = cntr - 0.02 * Point(static_cast<int>(eigen_vecs[1].x * eigen_val[1]), 
        static_cast<int>(eigen_vecs[1].y * eigen_val[1]));
    
    drawAxis(img, cntr, p1, Scalar(02550), 1);
    drawAxis(img, cntr, p2, Scalar(2552550), 5);
    
    // orientation in radins
    double angle = atan2(eigen_vecs[0].y, eigen_vecs[0].x);
 
    return angle;
}
 
int main()
{
    Mat src = imread("img/pca_test1.jpg");
 
    if (!src.data || src.empty())
    {
        cout << "can't not load image" << endl;
        return EXIT_FAILURE;
    }
 
    imshow("Src", src);
 
    //Convert Image to grayScale
    Mat gray;
    cvtColor(src, gray, COLOR_BGR2GRAY);
 
    // Convert image to Binary
    Mat bw;
    threshold(gray, bw, 50.255., CV_THRESH_BINARY || CV_THRESH_OTSU);
    
    // Find all the contours in the thresholded image
    vector<Vec4i> hierarchy;
    vector<vector<Point>> contours;
    findContours(bw, contours, hierarchy, CV_RETR_LIST, CV_CHAIN_APPROX_NONE);
 
    for (size_t i = 0; i < contours.size(); ++i)
    {
        // Calculate the area of each contour
        double area = contourArea(contours[i]);
 
        if (area < 1e2 || 1e5 < area)
            continue;
 
        drawContours(src, contours, static_cast<int>(i), Scalar(00255), 28, hierarchy, 0);
        getOrientation(contours[i], src);
    }
 
    imshow("output", src);
    
    waitKey(0);
    return 0;
}
cs





OpenCV Scene Change Detection(장면 전환 검출)

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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
// http://docs.opencv.org/3.0.0/d5/dc4/tutorial_video_input_psnr_ssim.html
// Base Examples : Video Input with OpenCV and similarity measurement
 
#include <cv.hpp>
#include <string>
#include <iostream>
 
using namespace std;
using namespace cv;
 
double getPSNR(const Mat& I1, const Mat& I2)
{
    Mat s1;
    absdiff(I1, I2, s1);       // |I1 - I2|
    s1.convertTo(s1, CV_32F);  // cannot make a square on 8 bits
    s1 = s1.mul(s1);           // |I1 - I2|^2
    Scalar s = sum(s1);        // sum elements per channel
    double sse = s.val[0+ s.val[1+ s.val[2]; // sum channels
 
    if (sse <= 1e-10// for small values return zero
        return 0;
    
    double mse = sse / (double)(I1.channels() * I1.total());
    double psnr = 10.0*log10((255 * 255/ mse);
    return psnr;
}
 
void merge(const Mat &m1, const Mat &m2, Mat &result)
{
    resize(result, result, Size(m1.cols + m2.cols, m1.rows));
    
    m1.copyTo(result(Rect(00, m1.cols, m1.rows)));
    m2.copyTo(result(Rect(m1.cols, 0, m2.cols, m2.rows)));
 
    putText(result, "Normal Video", cvPoint(3030),
        FONT_HERSHEY_COMPLEX_SMALL, 1.0, cvScalar(200200250), 1, CV_AA);
 
    putText(result, "Scene Change Detection", cvPoint(m1.cols + 3030),
        FONT_HERSHEY_COMPLEX_SMALL, 1.0, cvScalar(200200250), 1, CV_AA);
}
 
int main()
{
    stringstream conv;
    char c;
    int frameNum = -1// Frame counter
    double psnrV, CHANGE_DETECT_RATIO = 15.0;
    string videoPath = "video/Megamind.avi";
 
    VideoCapture cap(videoPath);
        
    // file open
    if (!cap.isOpened()) {
        cout << "Could not open video - " << videoPath << endl;
        return -1;
    }
    Size s = Size((int)cap.get(CAP_PROP_FRAME_WIDTH), (int)cap.get(CAP_PROP_FRAME_HEIGHT));
    Mat prevFrame, currFrame, changeFrame, result(s, CV_8UC3);
    
    namedWindow("Scene Change Detection");
    resizeWindow("Scene Change Detection", s.width * 2, s.height);
    
    while (1)
    {
        ++frameNum;
        cap >> currFrame;
 
        if (frameNum < 1) {
            prevFrame = currFrame.clone();
            changeFrame = currFrame.clone();
            continue;
        }
        
        if (currFrame.rows == 0 && currFrame.cols == 0)
            break;
 
        psnrV = getPSNR(prevFrame, currFrame);
        
        if (psnrV < CHANGE_DETECT_RATIO)
            changeFrame = currFrame.clone();
        
        merge(currFrame, changeFrame, result);
        imshow("Scene Change Detection", result);
 
        if (frameNum % 2 == 0)
            prevFrame = currFrame.clone();
 
        c = (char)waitKey(10);
        
        if (c == 27)
            break;
    }
 
    return 0;
}
cs




Image Segmentation with Distance Transform and Watershed Algorithm


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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
#include <cv.hpp>
#include <iostream>
 
using namespace std;
using namespace cv;
 
int DELAY_CAPTION = 1500;
char window_name[] = "Watershed Demo";
Mat src;
 
int display_caption(char *caption, Mat input);
 
int main()
{
    src = imread("img/card2.png");
 
    if (!src.data)
        return -1;
        
    display_caption("source Image", src);
 
    for (int x = 0; x < src.rows; x++) {
        for (int y = 0; y < src.cols; y++) {
            if (src.at<Vec3b>(x, y) == Vec3b(255255255)) {
                src.at<Vec3b>(x, y)[0= 0;
                src.at<Vec3b>(x, y)[1= 0;
                src.at<Vec3b>(x, y)[2= 0;
            }
        }
    }
 
    Mat kernel = (Mat_<float>(3,3<< 111,
                                      1-81,
                                      111);
 
    Mat imgLaplacian;
    Mat sharp = src;
    filter2D(sharp, imgLaplacian, CV_32F, kernel);
    src.convertTo(sharp, CV_32F);
    Mat imgResult = sharp - imgLaplacian;
 
    imgResult.convertTo(imgResult, CV_8UC3);
    imgLaplacian.convertTo(imgLaplacian, CV_8UC3);
 
    display_caption("New Sharped Image", imgResult);
    display_caption("New Sharped Image Laplacian", imgLaplacian);
 
    src = imgResult;
 
    Mat bw;
    cvtColor(src, bw, CV_BGR2GRAY);
    threshold(bw, bw, 40255, CV_THRESH_BINARY | CV_THRESH_OTSU);
 
    Mat dist;
 
    // CV_DIST_L2 = Euclidean distance
    distanceTransform(bw, dist, CV_DIST_L2, 3);
 
    normalize(dist, dist, 01., NORM_MINMAX);
    display_caption("Distance Transform Image", dist);
 
    threshold(dist, dist, .41., CV_THRESH_BINARY);
 
    Mat kernel1 = Mat::ones(33, CV_8UC1);
    dilate(dist, dist, kernel1);
    display_caption("peaks point", dist);
 
    Mat dist_8U;
    dist.convertTo(dist_8U, CV_8U);
    
    vector<vector<Point>> contours;
    findContours(dist_8U, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE);
 
    Mat markers = Mat::zeros(dist.size(), CV_32SC1);
 
    for (size_t i = 0; i < contours.size(); i++)
        drawContours(markers, contours, static_cast<int>(i), Scalar::all(static_cast<int>(i)+1));
 
    circle(markers, Point(55), 3, CV_RGB(255255255), -1);
    display_caption("Marker", markers * 10000);
 
    watershed(src, markers);
 
    Mat mark = Mat::zeros(markers.size(), CV_8UC1);
    markers.convertTo(mark, CV_8UC1);
    bitwise_not(mark, mark);
    
    display_caption("Markers_v2", mark);
 
    vector<Vec3b>colors;
 
    for (size_t i = 0; i < contours.size(); i++)
    {
        int b = theRNG().uniform(0255);
        int g = theRNG().uniform(0255);
        int r = theRNG().uniform(0255);
 
        colors.push_back(Vec3b((uchar)b, (uchar)g, (uchar)r));
    }
 
    Mat dst = Mat::zeros(markers.size(), CV_8UC3);
 
    for (int i = 0; i < markers.rows; i++)
    {
        for (int j = 0; j < markers.cols; j++)
        {
            int index = markers.at<int>(i, j);
            if (index > 0 && index <= static_cast<int>(contours.size()))
                dst.at<Vec3b>(i, j) = colors[index - 1];
            else
                dst.at<Vec3b>(i, j) = Vec3b(000);
        }
    }
 
    display_caption("Final", dst);
 
    waitKey(0); 
    
    return 0;
}
 
int display_caption(char *caption, Mat input)
{
    Mat dst;
    dst = Mat::zeros(input.size(), input.type());
    
    putText(dst, caption, Point(src.cols / 4, src.rows / 2), FONT_HERSHEY_COMPLEX, 1, Scalar(255255255));
 
    imshow(window_name, dst);
    int c = waitKey(DELAY_CAPTION);
 
    if (c >= 0)
        return -1;
 
    imshow(window_name, input);
    
    c = waitKey(DELAY_CAPTION);
 
    if (c >= 0)
        return -1;
    return 0;
}
 
cs

 




OpenCV Image Filtering

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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
// Reference : http://docs.opencv.org/3.0.0/dc/dd3/tutorial_gausian_median_blur_bilateral_filter.html
// Examples : Smoothing Images
 
#include <iostream>
#include <cv.hpp>
#include <windows.h>
 
using namespace std;
using namespace cv;
 
int DELAY_CAPTION = 1500;
int DELAY_BLUR = 100;
int MAX_KERNEL_LENGTH = 31;
 
int display_caption(char *caption);
int display_dst(int delay);
char window_name[] = "Filter Demo 1";
 
Mat src, dst;
 
int main()
{
    namedWindow(window_name);
    
    Sleep(2000);
 
    src = imread("img/lena.bmp");
 
    if (display_caption("Original Image"!= 0)
        return 0;
 
    dst = src.clone();
    if (display_dst(DELAY_CAPTION) != 0)
        return 0;
    
    
    for (int i = 1; i < MAX_KERNEL_LENGTH; i = i + 2)
    {
        blur(src, dst, Size(i, i), Point(-1-1));
        if (display_dst(DELAY_BLUR) != 0)
            return 0;
 
        if (display_caption("Gaussian Blur"!= 0)
            return 0;
 
        for (int i = 1; i < MAX_KERNEL_LENGTH; i = i + 2)
        {
            GaussianBlur(src, dst, Size(i, i), 00);
            if (display_dst(DELAY_BLUR) != 0)
                return 0;
        }
 
        if (display_caption("Median Blur"!= 0)
            return 0;
 
        for (int i = 1; i < MAX_KERNEL_LENGTH; i = i + 2)
        {
            medianBlur(src, dst, i);
            if (display_dst(DELAY_BLUR) != 0)
                return 0;
        }
 
        if (display_caption("Bilateral Blur"!= 0)
            return 0;
        
        for (int i = 1; i < MAX_KERNEL_LENGTH; i = i + 2)
        {
            bilateralFilter(src, dst, i, i * 2, i / 2);
            if (display_dst(DELAY_BLUR) != 0)
                return 0;
        }
 
        display_caption("End");
 
        waitKey(0);
        return 0;
    }
}
 
int display_caption(char *caption)
{
    dst = Mat::zeros(src.size(), src.type());
    putText(dst, caption, Point(src.cols / 4, src.rows / 2), FONT_HERSHEY_COMPLEX, 1, Scalar(255255255));
 
    imshow(window_name, dst);
    int c = waitKey(DELAY_CAPTION);
    if (c >= 0)
        return -1;
    
    return 0;
}
 
int display_dst(int delay)
{
    imshow(window_name, dst);
    int c = waitKey(delay);
    if (c >= 0)
        return -1;
    
    return 0;
}
cs





OpenCV 엠보싱, 수채화, 컬러 스케치 효과

OpenCV를 이용하여 엠보싱효과, 수채화 효과, 컬러 스케치 효과를 적용해 보았다.


                                            [그림 1] 기본 이미지


                                        [그림 2] 엠보싱 효과 적용


                                       [그림 3] 수채화 효과 적용


                                   [그림 4] 컬러 스케치 효과 적용


# 코드

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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
#include <cv.h>
#include <highgui.h>
#include <iostream>
 
using namespace std;
using namespace cv;
 
void embose(Mat &), sketch(Mat &), waterColor(Mat &), display(Mat &, String);
 
int main()
{
    Mat image;
    image = imread("flower.jpg");
    
    if (image.empty())
        return -1;
    
    namedWindow("pure image");
    imshow("pure image", image);
    
    embose(image);
    sketch(image);
    waterColor(image);
 
    waitKey(0);
 
}
 
void embose(Mat &src)
{
    /* embose mask
      -1 0 0
       0 0 0
       0 0 1  */
    Mat mask(3, 3, CV_32F, Scalar(0)), dst;
 
    mask.at<float>(0, 0) = -1.0;
    mask.at<float>(2, 2) = 1.0;
    
    filter2D(src, dst, CV_16S, mask);
    dst.convertTo(dst, CV_8U, 1, 128);
 
    display(dst, "embose");
}
 
void sketch(Mat &src)
{
    Mat gray, edge, dst;
    int threshold = 5;
 
    cvtColor(src, gray, CV_BGR2GRAY);
    blur(gray, gray, Size(3, 3));
    Canny(gray, edge, threshold, threshold * 2, 5, 3);
    
    dst.create(src.size(), src.type());
    dst = Scalar::all(128);
    src.copyTo(dst, edge);
    
    display(dst, "sketch");
}
 
void waterColor(Mat &src)
{
    double dsize = 7, sigma = 32;
    int iterate = 20;
    
    Mat t1 = src.clone(), t2;
    
    for (int i = 0; i < iterate; i++)
    {
        if (i % 2 == 0)
            bilateralFilter(t1, t2, dsize, sigma, sigma);
        else
            bilateralFilter(t2, t1, dsize, sigma, sigma);
    }
    if (iterate % 2 == 0)
        display(t1, "waterColor");
    else
        display(t2, "waterColor");
}
 
void display(Mat &img, String name)
{
    namedWindow(name);
    imshow(name, img);
}
 



사람얼굴을 검출해보자 (OpenCV)

사람은 각종 배경 여러 물건이 혼합 된 이미지 속에서 사람 얼굴을 쉽게 찾아낸다. 

주변에는 인공지능 까지는 아니더라도 사용자를 알아보는 사례가 많다. 인터넷 쇼핑 사이트에서 자신이 검색한 물건

위주로 정렬해 다시 접속시 보여주는 기능도 한가지 예가 될것이다. 컴퓨터가 얼굴을 인식하도록 하는 방법은 없을까? 

다행이도 OpenCV 라이브러리에서 SDK형태로 제공해 쉽게 테스트 해볼수 있다.

다음은 OpenCV에서 제공하는 얼굴 인식 코드 이다.


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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
#include <cv.h>
#include <highgui.h>
#include <iostream>
 
using namespace std;
using namespace cv;
 
String face_cascade = "C:/opencv/sources/data/haarcascades/haarcascade_frontalface_alt.xml";
String eye_cascade = "C:/opencv/sources/data/haarcascades/haarcascade_eye.xml";
String img_name = "face.jpg"// 파일 명
 
CascadeClassifier face;
CascadeClassifier eye;
 
int main()
{
    Mat img = imread(img_name);
 
    if (img.data == NULL) {
        cout << img_name << " 이미지 열기 실패" << endl;
        return -1;
    }
 
    if (!face.load(face_cascade) || !eye.load(eye_cascade)) {
        cout << "Cascade 파일 열기 실패" << endl;
        return -1;
    }
    
    #pragma region 얼굴 검출
    Mat gray;
    cvtColor(img, gray, CV_RGB2GRAY);
 
    vector<Rect> face_pos; // 얼굴 위치 저장
    face.detectMultiScale(gray, face_pos, 1.13| CV_HAAR_SCALE_IMAGE, Size(1010)); // 얼굴 검출
 
    // 얼굴 영역 표시
    for (int i = 0; i < (int)face_pos.size(); i++)    {
        rectangle(img, face_pos[i], Scalar(02550), 2);
    }
    #pragma endregion
 
 
    #pragma region 눈 검출
    for (int i = 0; i < (int)face_pos.size(); i++) {
        vector<Rect> eye_pos; // 눈 위치 저장
        
        Mat roi = gray(face_pos[i]); // 관심영역 설정
        eye.detectMultiScale(roi, eye_pos, 1.13| CV_HAAR_SCALE_IMAGE, Size(1010)); // 눈 검출
 
        // 눈 영역 표시
        for (int j = 0; j < (int)eye_pos.size(); j++) {
            Point center(face_pos[i].x + eye_pos[j].x + (eye_pos[j].width / 2), 
                       face_pos[i].y + eye_pos[j].y + (eye_pos[j].height / 2));
            
            int radius = cvRound((eye_pos[j].width + eye_pos[j].height) * 0.2);
            circle(img, center, radius, Scalar(00255), 2); 
        }
    }
    #pragma endregion
 
    namedWindow("검출");
    imshow("검출", img);
        
    waitKey();
    return 0;
}
cs


                                              [그림 1] 얼굴 검출


       [그림 2] 얼굴 검출 & 눈 검출


결과를 보면 얼굴과 눈 검출이 매우 잘됨을 확인할 수 있었다. 

소스를 보면 매우 간단한 구조이다. 이미지와 cascade파일을 불러오고 검출 함수를 통해 출력하는 구조이다.

함수 하나만으로 저렇게 잘 동작한다니 과연 저 함수는 어떻게 동작하는 것일까?


함수는 어떤 파일을 불러와 동작하는것을 확인할 수 있다. 불러온 파일에 따라 얼굴이 검출되기도 하고,

눈이 검출되기도 했다. 불러온 파일을 보니 haarcascade_xxx 가 붙어 있다. 뒤에 오는것에 따라 

얼굴과 눈이 검출됬다. 과연 haarcascade가 무엇일까? 네이버 사전을 검색해보면 cascade는 

작은폭포? 풍성하게 늘어진것 같은 뜻을 지닌다. 그럼 Haar는 무엇일까?

2001년 두 학자(Viola, Jones)가  "Rapid Object Detection using a Boosted Cascade of Simple Features" 

논문에서 제안한 방법이다. 이들은 유사 하르 특징(Haar like feature)이라는 것에 기반한  알고리즘으로 구성

되어 있었다. 

Haar 특징에 대해 인터넷에 검색해보면 아래와 같은 사진을 볼 수 있다.


                                      [그림 3] Haar like feature


저 사각형은 무엇인가? 뭔지 몰라서 한참을 들여다 봤다. 알고보니 사람의 얼굴에는 뭔가 특별한 패턴이 있는것을

알수 있다. 두 눈은 명암이 어둡고 코는 명암이 밝다. 이런 명암을 이용해 패턴을 구하는 것이다. 

이것들을 Haar like feature라고 한다. 사람의 얼굴 위에 흑백의 사각형을 겹쳐 놓은 다음 밝은 영역에 속한 픽셀

값들의 평균에서 어두운 영역에 속한 픽셀값들의 평균의 차이를 구한다. 그 차이가 문턱값(threshold)를 넘으면

사람 얼굴에 대한 유사 하르 특징이 있는것이었다. 사람의 얼굴은 다양하지만 생김새의 패턴은 비슷하므로 

임의의 얼굴 위에서의 특정 위치, 특정 분포에 따른 명암의 차이는 거의 없을것이라고 판단한 것이다. 


              [그림 4] haarcascade_frontalface.xml 일부


위 그림은 haarcascade_frontalface.xml 파일의 일부이다. 태그 <size> 20 20은 얼굴의 크기이고 그 안의 영역에

[그림 3]의 사각형이 돌아다니면서 명암의 평균 차를 측정하는 것이었다. [그림 4]의 rect값인 3 7 14 4 -1은 순서

대로 x좌표, y좌표, 폭(width), 높이(height), 이득(gain)이다. 그리고 마지막 값은 부호를 나타낸다. -1이면 픽셀값의

평균을 빼라는 뜻이고, 2는 2를 곱한후 더하란 뜻이된다. <threshold>는 임계값(문턱치)가 될것이다.