opencv模板匹配

1.腐蚀操作

#include <iostream>
#include <opencv2\opencv.hpp>

using namespace cv;
using namespace std;
int main()
{
	Mat SrcPic = imread("lena.jpg");
	imshow("Src Pic", SrcPic);
	/*创建结构元素的函数。"MORPH_RECT"表示创建一个矩形形状的结构元素。"MORPH_ELLIPSE"椭圆形,"MORPH_CROSS"十字形
	* Size(15,15)是指定结构元素大小的参数,以像素为单位
	*/
	Mat element = getStructuringElement(MORPH_RECT, Size(15, 15)); 
	Mat DstPic;
	erode(SrcPic, DstPic, element); //腐蚀操作。element:定义腐蚀操作的内核形状和大小
	imshow("腐蚀效果图", DstPic);
	waitKey();
	return 0;
}

2.canny边缘检测

思路:将原始图像转化为灰度图,用blur函数进行图像模糊以降噪,然后用canny函数进行边缘检测。

#include <iostream>
#include <opencv2\opencv.hpp>

using namespace cv;
using namespace std;
int main()
{
	Mat SrcPic = imread("lena.jpg");
	imshow("Src Pic", SrcPic);
	Mat DstPic, edge, grayImage;

	//创建与src同类型和同大小的矩阵
	DstPic.create(SrcPic.size(), SrcPic.type());
	
	//将原始图转化为灰度图
	cvtColor(SrcPic, grayImage, COLOR_BGR2GRAY);

	//先使用3*3内核来降噪
	blur(grayImage, edge, Size(3, 3));

	//运行canny算子
	Canny(edge, edge, 3, 9, 3);

	imshow("边缘提取效果", edge);

	waitKey();
	return 0;
}

3.膨胀,腐蚀,开闭运算

腐蚀和膨胀是最基本的形态学运算。腐蚀和膨胀是针对白色部分(高亮部分)而言的。膨胀就是对图像高亮部分进行“领域扩张”,效果图拥有比原图更大的高亮区域;腐蚀是原图中的高亮区域被蚕食,效果图拥有比原图更小的高亮区域。

开运算:先腐蚀再膨胀,用来消除小物体
闭运算:先膨胀再腐蚀,用于排除小型黑洞
形态学梯度:就是膨胀图与俯视图之差,用于保留物体的边缘轮廓。
顶帽:原图像与开运算图之差,用于分离比邻近点亮一些的斑块。
黑帽:闭运算与原图像之差,用于分离比邻近点暗一些的斑块。

opencv里有一个很好的函数getStructuringElement,我们只要往这个函数传相应的处理参数,就可以进行相应的操作了,使用起来非常方便。下面列举一下相应的操作宏定义。
在这里插入图片描述

#include<opencv2\opencv.hpp>   
#include<opencv2\highgui\highgui.hpp>

using namespace std;
using namespace cv;

//高级形态学处理
int main()
{
	Mat img = imread("lol1.jpg");
	namedWindow("原始图", WINDOW_NORMAL);
	imshow("原始图", img);
	Mat out;
	//获取自定义核
	Mat element = getStructuringElement(MORPH_RECT, Size(15, 15)); //第一个参数MORPH_RECT表示矩形的卷积核,当然还可以选择椭圆形的、交叉型的	
	//高级形态学处理,调用这个函数就可以了,具体要选择哪种操作,就修改第三个参数就可以了。这里演示的是形态学梯度处理
	morphologyEx(img, out, MORPH_GRADIENT, element);
	namedWindow("形态学处理操作", WINDOW_NORMAL);
	imshow("形态学处理操作", out);
	waitKey(0);

}

4.模板匹配

#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <iostream>
#include <stdio.h>

using namespace std;
using namespace cv;

int main()
{
	Mat img, templ, result;
	img = imread("nba.jpg");
	templ = imread("76.png");

	int result_cols = img.cols - templ.cols + 1;
	int result_rows = img.rows - templ.rows + 1;
	result.create(result_cols, result_rows, CV_32FC1);

	matchTemplate(img, templ, result, CV_TM_SQDIFF_NORMED);//这里我们使用的匹配算法是标准平方差匹配 method=CV_TM_SQDIFF_NORMED,数值越小匹配度越好
	normalize(result, result, 0, 1, NORM_MINMAX, -1, Mat());

	double minVal = -1;
	double maxVal;
	Point minLoc;
	Point maxLoc;
	Point matchLoc;
	cout << "匹配度:" << minVal << endl;
	//result:结果矩阵;minVal:最小值;maxVal:最大值;minLoc:最小值坐标;maxLoc:最大值坐标
	minMaxLoc(result, &minVal, &maxVal, &minLoc, &maxLoc, Mat());

	cout << "匹配度:" << minVal << endl;
	matchLoc = minLoc;
	rectangle(img, matchLoc, Point(matchLoc.x + templ.cols, matchLoc.y + templ.rows), Scalar(0, 255, 0), 2, 8, 0);

	imshow("img", img);
	waitKey(0);

	return 0;
}

匹配方法一共有6种

5.角点检测

Harris角点检测:Harris角点检测是一种直接基于灰度图的角点提取算法,稳定性高,尤其对L型角点(也就是直角)检测精度高。缺点也是明显的,就是运算速度慢。

#include <opencv2/opencv.hpp>  

using namespace cv;
using namespace std;

Mat g_srcImage, g_srcImage1, g_grayImage;
int thresh = 30; //当前阈值  
int max_thresh = 175; //最大阈值  

void on_CornerHarris(int, void*);//回调函数  

int main(int argc, char** argv)
{
	g_srcImage = imread("1.jpg", 1);
	if (!g_srcImage.data)
	{
		printf("读取图片错误! \n");
		return -1;
	}
	imshow("原始图", g_srcImage);
	g_srcImage1 = g_srcImage.clone();

	//存留一张灰度图  
	cvtColor(g_srcImage1, g_grayImage, COLOR_BGR2GRAY);

	//创建窗口和滚动条  
	namedWindow("角点检测", WINDOW_AUTOSIZE);
	createTrackbar("阈值: ", "角点检测", &thresh, max_thresh, on_CornerHarris);

	//调用一次回调函数,进行初始化  
	on_CornerHarris(0, 0);

	waitKey(0);
	return(0);
}


void on_CornerHarris(int, void*)
{
	Mat dstImage;//目标图  
	Mat normImage;//归一化后的图  
	Mat scaledImage;//线性变换后的八位无符号整型的图  

	//置零当前需要显示的两幅图,即清除上一次调用此函数时他们的值  
	dstImage = Mat::zeros(g_srcImage.size(), CV_32FC1);
	g_srcImage1 = g_srcImage.clone();

	//进行角点检测  
	//第三个参数表示邻域大小,第四个参数表示Sobel算子孔径大小,第五个参数表示Harris参数
	cornerHarris(g_grayImage, dstImage, 2, 3, 0.04, BORDER_DEFAULT);

	// 归一化与转换  
	normalize(dstImage, normImage, 0, 255, NORM_MINMAX, CV_32FC1, Mat());
	convertScaleAbs(normImage, scaledImage);//将归一化后的图线性变换成8位无符号整型   

	// 将检测到的,且符合阈值条件的角点绘制出来  
	for (int j = 0; j < normImage.rows; j++)
	{
		for (int i = 0; i < normImage.cols; i++)
		{
			//Mat::at<float>(j,i)获取像素值,并与阈值比较
			if ((int)normImage.at<float>(j, i) > thresh + 80)
			{
				circle(g_srcImage1, Point(i, j), 5, Scalar(10, 10, 255), 2, 8, 0);
				circle(scaledImage, Point(i, j), 5, Scalar(0, 10, 255), 2, 8, 0);
			}
		}
	}

	imshow("角点检测", g_srcImage1);
	imshow("角点检测2", scaledImage);

}

Shi-Tomasi角点检测:除了上述的Harris角点检测方法,我们还可以采用Shi-Tomasi方法进行角点检测。Shi-Tomsi算法是Harris算法的加强版,性能当然也有相应的提高。

6.图像矫正

7.图像修复

8.图像配准

main.cpp

// https://www.cnblogs.com/skyfsm/p/7253208.html

9.图像拼接

#include <opencv2/highgui.hpp>   
#include <opencv2/core.hpp>
#include <opencv2/features2d.hpp>
#include <opencv2/opencv.hpp>
#include <iostream>  
#include <opencv2/xfeatures2d/nonfree.hpp>

using namespace cv;
using namespace std;

void OptimizeSeam(Mat& img1, Mat& trans, Mat& dst);

typedef struct
{
    Point2f left_top;
    Point2f left_bottom;
    Point2f right_top;
    Point2f right_bottom;
}four_corners_t;

four_corners_t corners;

void CalcCorners(const Mat& H, const Mat& src)
{
    double v2[] = { 0, 0, 1 };//左上角
    double v1[3];//变换后的坐标值
    Mat V2 = Mat(3, 1, CV_64FC1, v2);  //列向量
    Mat V1 = Mat(3, 1, CV_64FC1, v1);  //列向量

    V1 = H * V2;
    //左上角(0,0,1)
    cout << "V2: " << V2 << endl;
    cout << "V1: " << V1 << endl;
    corners.left_top.x = v1[0] / v1[2];
    corners.left_top.y = v1[1] / v1[2];

    //左下角(0,src.rows,1)
    v2[0] = 0;
    v2[1] = src.rows;
    v2[2] = 1;
    V2 = Mat(3, 1, CV_64FC1, v2);  //列向量
    V1 = Mat(3, 1, CV_64FC1, v1);  //列向量
    V1 = H * V2;
    corners.left_bottom.x = v1[0] / v1[2];
    corners.left_bottom.y = v1[1] / v1[2];

    //右上角(src.cols,0,1)
    v2[0] = src.cols;
    v2[1] = 0;
    v2[2] = 1;
    V2 = Mat(3, 1, CV_64FC1, v2);  //列向量
    V1 = Mat(3, 1, CV_64FC1, v1);  //列向量
    V1 = H * V2;
    corners.right_top.x = v1[0] / v1[2];
    corners.right_top.y = v1[1] / v1[2];

    //右下角(src.cols,src.rows,1)
    v2[0] = src.cols;
    v2[1] = src.rows;
    v2[2] = 1;
    V2 = Mat(3, 1, CV_64FC1, v2);  //列向量
    V1 = Mat(3, 1, CV_64FC1, v1);  //列向量
    V1 = H * V2;
    corners.right_bottom.x = v1[0] / v1[2];
    corners.right_bottom.y = v1[1] / v1[2];

}

int main(int argc, char* argv[])
{
    Mat image01 = imread(".\\src_pic\\1.jpg", 1);    //右图
    Mat image02 = imread(".\\src_pic\\2.jpg", 1);    //左图
    imshow("p1", image01);
    imshow("p2", image02);

    //灰度图转换  
    Mat image1, image2;
    cvtColor(image01, image1, COLOR_RGB2GRAY);
    cvtColor(image02, image2, COLOR_RGB2GRAY);

    //提取特征点    
    //SurfFeatureDetector surfDetector(800);  // 海塞矩阵阈值,在这里调整精度,值越大点越少,越精准 
    Ptr<xfeatures2d::SURF> surfDetector = xfeatures2d::SURF::create(800);
    vector<KeyPoint> keyPoint1, keyPoint2;
    surfDetector->detect(image1, keyPoint1);
    surfDetector->detect(image2, keyPoint2);

    //特征点描述,为下边的特征点匹配做准备    
    Mat imageDesc1, imageDesc2;
    surfDetector->compute(image1, keyPoint1, imageDesc1);
    surfDetector->compute(image2, keyPoint2, imageDesc2);

    //获得匹配特征点,并提取最优配对     
    FlannBasedMatcher matcher;
    vector<DMatch> matchePoints;
    matcher.match(imageDesc1, imageDesc2, matchePoints, Mat());
    cout << "**********************************************" << endl;
    cout << "total match points: " << matchePoints.size() << endl;
    // sort(matchePoints.begin(), matchePoints.end()); //特征点排序    

    Mat img_match;

    drawMatches(image01, keyPoint1, image02, keyPoint2, matchePoints, img_match);
    imshow("match points", img_match);

    //获取排在前N个的最优匹配特征点  
    vector<Point2f> imagePoints1, imagePoints2;

    for (int i = 0; i < matchePoints.size(); i++)
    {
        imagePoints1.push_back(keyPoint1[matchePoints[i].queryIdx].pt);
        imagePoints2.push_back(keyPoint2[matchePoints[i].trainIdx].pt);
    }



    //获取图像1到图像2的投影映射矩阵 尺寸为3*3  
    Mat homo = findHomography(imagePoints1, imagePoints2, RANSAC);
    也可以使用getPerspectiveTransform方法获得透视变换矩阵,不过要求只能有4个点,效果稍差  
    //Mat   homo=getPerspectiveTransform(imagePoints1,imagePoints2);  
    cout << "变换矩阵为:\n" << homo << endl << endl; //输出映射矩阵      

    //计算配准图的四个顶点坐标
    CalcCorners(homo, image01);
    cout << "left_top:" << corners.left_top << endl;
    cout << "left_bottom:" << corners.left_bottom << endl;
    cout << "right_top:" << corners.right_top << endl;
    cout << "right_bottom:" << corners.right_bottom << endl;

    //图像配准  
    Mat imageTransform1, imageTransform2;
    warpPerspective(image01, imageTransform1, homo, Size(MAX(corners.right_top.x, corners.right_bottom.x), image02.rows));
    //warpPerspective(image01, imageTransform2, adjustMat*homo, Size(image02.cols*1.3, image02.rows*1.8));
    imshow("直接经过透视矩阵变换", imageTransform1);
    imwrite(".\\dst_pic\\trans1.jpg", imageTransform1);


    //创建拼接后的图,需提前计算图的大小
    int dst_width = imageTransform1.cols;  //取最右点的长度为拼接图的长度
    int dst_height = image02.rows;

    Mat dst(dst_height, dst_width, CV_8UC3);
    dst.setTo(0);

    imageTransform1.copyTo(dst(Rect(0, 0, imageTransform1.cols, imageTransform1.rows)));
    image02.copyTo(dst(Rect(0, 0, image02.cols, image02.rows)));

    OptimizeSeam(image02, imageTransform1, dst);


    imshow("dst", dst);
    imwrite(".\\dst_pic\\dst.jpg", dst);

    waitKey();

    return 0;
}


//优化两图的连接处,使得拼接自然
void OptimizeSeam(Mat& img1, Mat& trans, Mat& dst)
{
    int start = MIN(corners.left_top.x, corners.left_bottom.x);//开始位置,即重叠区域的左边界  

    double processWidth = img1.cols - start;//重叠区域的宽度  
    int rows = dst.rows;
    int cols = img1.cols; //注意,是列数*通道数
    double alpha = 1;//img1中像素的权重  
    for (int i = 0; i < rows; i++)
    {
        uchar* p = img1.ptr<uchar>(i);  //获取第i行的首地址
        uchar* t = trans.ptr<uchar>(i);
        uchar* d = dst.ptr<uchar>(i);
        for (int j = start; j < cols; j++)
        {
            if (t[j * 3] == 0 && t[j * 3 + 1] == 0 && t[j * 3 + 2] == 0)
            {
                alpha = 1;
            }
            else
            {
                alpha = (processWidth - (j - start)) / processWidth;
            }

            d[j * 3] = p[j * 3] * alpha + t[j * 3] * (1 - alpha);
            d[j * 3 + 1] = p[j * 3 + 1] * alpha + t[j * 3 + 1] * (1 - alpha);
            d[j * 3 + 2] = p[j * 3 + 2] * alpha + t[j * 3 + 2] * (1 - alpha);

        }
    }

}

10.全能扫描王

#include <iostream>
#include <opencv2\opencv.hpp>
#include <opencv2\imgproc\imgproc.hpp>
#include <algorithm>

using namespace std;
using namespace cv;

cv::Point2f center(0, 0);

bool sort_corners(std::vector<cv::Point2f>& corners)
{
    std::vector<cv::Point2f> top, bot;
    cv::Point2f tmp_pt;
    std::vector<cv::Point2f> olddata = corners;

    if (corners.size() != 4)
    {
        return false;
    }

    for (size_t i = 0; i < corners.size(); i++)
    {
        for (size_t j = i + 1; j < corners.size(); j++)
        {
            if (corners[i].y < corners[j].y)
            {
                tmp_pt = corners[i];
                corners[i] = corners[j];
                corners[j] = tmp_pt;
            }
        }
    }
    top.push_back(corners[0]);
    top.push_back(corners[1]);
    bot.push_back(corners[2]);
    bot.push_back(corners[3]);
    if (top.size() == 2 && bot.size() == 2) {
        corners.clear();
        cv::Point2f tl = top[0].x > top[1].x ? top[1] : top[0];
        cv::Point2f tr = top[0].x > top[1].x ? top[0] : top[1];
        cv::Point2f bl = bot[0].x > bot[1].x ? bot[1] : bot[0];
        cv::Point2f br = bot[0].x > bot[1].x ? bot[0] : bot[1];
        corners.push_back(tl);
        corners.push_back(tr);
        corners.push_back(br);
        corners.push_back(bl);
        return true;
    }
    else
    {
        corners = olddata;
        return false;
    }
}

cv::Point2f computeIntersect(cv::Vec4i a, cv::Vec4i b)
{
    int x1 = a[0], y1 = a[1], x2 = a[2], y2 = a[3];
    int x3 = b[0], y3 = b[1], x4 = b[2], y4 = b[3];

    if (float d = ((float)(x1 - x2) * (y3 - y4)) - ((y1 - y2) * (x3 - x4)))
    {
        cv::Point2f pt;
        pt.x = ((x1 * y2 - y1 * x2) * (x3 - x4) - (x1 - x2) * (x3 * y4 - y3 * x4)) / d;
        pt.y = ((x1 * y2 - y1 * x2) * (y3 - y4) - (y1 - y2) * (x3 * y4 - y3 * x4)) / d;
        return pt;
    }
    else
        return cv::Point2f(-1, -1);
}

bool IsBadLine(int a, int b)
{
    if (a * a + b * b < 100)
    {
        return true;
    }
    else
    {
        return false;
    }
}

bool x_sort(const Point2f& m1, const Point2f& m2)
{
    return m1.x < m2.x;
}

//确定四个点的中心线
void sortCorners(vector<Point2f>& corners,Point2f center)
{
    vector<Point2f> top, bot;
    vector<Point2f> backup = corners;

    //sort(corners, x_sort);  //注意先按x的大小给4个点排序
    std::sort(corners.begin(), corners.end(), [](const cv::Point2f& p1, const cv::Point2f& p2) {
        return p1.x < p2.x;
        });

    for (int i = 0; i < corners.size(); i++)
    {
        if (corners[i].y < center.y&& top.size() < 2)    //这里的小于2是为了避免三个顶点都在top的情况
            top.push_back(corners[i]);
        else
            bot.push_back(corners[i]);
    }
    corners.clear();

    if (top.size() == 2 && bot.size() == 2)
    {
        cout << "log" << endl;
        cv::Point2f tl = top[0].x > top[1].x ? top[1] : top[0];
        cv::Point2f tr = top[0].x > top[1].x ? top[0] : top[1];
        cv::Point2f bl = bot[0].x > bot[1].x ? bot[1] : bot[0];
        cv::Point2f br = bot[0].x > bot[1].x ? bot[0] : bot[1];


        corners.push_back(tl);
        corners.push_back(tr);
        corners.push_back(br);
        corners.push_back(bl);
    }
    else
    {
        corners = backup;
    }
}

int g_dst_hight;  //最终图像的高度
int g_dst_width; //最终图像的宽度

void CalcDstSize(const vector<cv::Point2f>& corners)
{
    int h1 = sqrt((corners[0].x - corners[3].x) * (corners[0].x - corners[3].x) + (corners[0].y - corners[3].y) * (corners[0].y - corners[3].y));
    int h2 = sqrt((corners[1].x - corners[2].x) * (corners[1].x - corners[2].x) + (corners[1].y - corners[2].y) * (corners[1].y - corners[2].y));
    g_dst_hight = MAX(h1, h2);

    int w1 = sqrt((corners[0].x - corners[1].x) * (corners[0].x - corners[1].x) + (corners[0].y - corners[1].y) * (corners[0].y - corners[1].y));
    int w2 = sqrt((corners[2].x - corners[3].x) * (corners[2].x - corners[3].x) + (corners[2].y - corners[3].y) * (corners[2].y - corners[3].y));
    g_dst_width = MAX(w1, w2);
}

int main()
{
    Mat src = imread("20.png");
    imshow("src img", src);
    Mat source = src.clone();

    Mat bkup = src.clone();

    Mat img = src.clone();
    cvtColor(img, img, COLOR_RGB2GRAY);   //二值化
    imshow("gray", img);
    //equalizeHist(img, img);
    //imshow("equal", img);
    GaussianBlur(img, img, Size(5, 5), 0, 0);  //高斯滤波

    //获取自定义核
    Mat element = getStructuringElement(MORPH_RECT, Size(3, 3)); //第一个参数MORPH_RECT表示矩形的卷积核,当然还可以选择椭圆形的、交叉型的
    //膨胀操作
    dilate(img, img, element);  //实现过程中发现,适当的膨胀很重要
    imshow("dilate", img);
    Canny(img, img, 30, 120, 3);   //边缘提取
    imshow("get contour", img);

    vector<vector<Point> > contours;
    vector<vector<Point> > f_contours;
    std::vector<cv::Point> approx2;
    //注意第5个参数为CV_RETR_EXTERNAL,只检索外框  
    findContours(img, f_contours, RETR_EXTERNAL, CHAIN_APPROX_NONE); //找轮廓

    //求出面积最大的轮廓
    int max_area = 0;
    int index;
    for (int i = 0; i < f_contours.size(); i++)
    {
        double tmparea = fabs(contourArea(f_contours[i]));
        if (tmparea > max_area)
        {
            index = i;
            max_area = tmparea;
        }

    }
    contours.push_back(f_contours[index]);

    cout << contours.size() << endl;  //因为我写的是找出最外层轮廓,所以理论上只有一个轮廓

    vector<Point> tmp = contours[0];

    for (int line_type = 1; line_type <= 3; line_type++)
    {
        cout << "line_type: " << line_type << endl;
        Mat black = img.clone();
        black.setTo(0);
        drawContours(black, contours, 0, Scalar(255), line_type);  //注意线的厚度,不要选择太细的
        imshow("show contour", black);


        std::vector<Vec4i> lines;
        std::vector<cv::Point2f> corners;
        std::vector<cv::Point2f> approx;

        int para = 10;
        int flag = 0;
        int round = 0;
        for (; para < 300; para++)
        {
            cout << "round: " << ++round << endl;
            lines.clear();
            corners.clear();
            approx.clear();
            center = Point2f(0, 0);

            cv::HoughLinesP(black, lines, 1, CV_PI / 180, para, 30, 10);

            //过滤距离太近的直线
            std::set<int> ErasePt;
            for (int i = 0; i < lines.size(); i++)
            {
                for (int j = i + 1; j < lines.size(); j++)
                {
                    if (IsBadLine(abs(lines[i][0] - lines[j][0]), abs(lines[i][1] - lines[j][1])) && (IsBadLine(abs(lines[i][2] - lines[j][2]), abs(lines[i][3] - lines[j][3]))))
                    {
                        ErasePt.insert(j);//将该坏线加入集合
                    }
                }
            }

            int Num = lines.size();
            while (Num != 0)
            {
                std::set<int>::iterator j = ErasePt.find(Num);
                if (j != ErasePt.end())
                {
                    lines.erase(lines.begin() + Num - 1);
                }
                Num--;
            }
            if (lines.size() != 4)
            {
                continue;
            }

            //计算直线的交点,保存在图像范围内的部分
            for (int i = 0; i < lines.size(); i++)
            {
                for (int j = i + 1; j < lines.size(); j++)
                {
                    cv::Point2f pt = computeIntersect(lines[i], lines[j]);
                    if (pt.x >= 0 && pt.y >= 0 && pt.x <= src.cols && pt.y <= src.rows)             //保证交点在图像的范围之内
                        corners.push_back(pt);
                }
            }
            if (corners.size() != 4)
            {
                continue;
            }
#if 1
            bool IsGoodPoints = true;

            //保证点与点的距离足够大以排除错误点
            for (int i = 0; i < corners.size(); i++)
            {
                for (int j = i + 1; j < corners.size(); j++)
                {
                    int distance = sqrt((corners[i].x - corners[j].x) * (corners[i].x - corners[j].x) + (corners[i].y - corners[j].y) * (corners[i].y - corners[j].y));
                    if (distance < 5)
                    {
                        IsGoodPoints = false;
                    }
                }
            }

            if (!IsGoodPoints) continue;
#endif
            cv::approxPolyDP(cv::Mat(corners), approx, cv::arcLength(cv::Mat(corners), true) * 0.02, true);

            if (lines.size() == 4 && corners.size() == 4 && approx.size() == 4)
            {
                flag = 1;
                break;
            }
        }

        // Get mass center  
        for (int i = 0; i < corners.size(); i++)
            center += corners[i];
        center *= (1. / corners.size());

        if (flag)
        {
            cout << "we found it!" << endl;
            cv::circle(bkup, corners[0], 3, CV_RGB(255, 0, 0), -1);
            cv::circle(bkup, corners[1], 3, CV_RGB(0, 255, 0), -1);
            cv::circle(bkup, corners[2], 3, CV_RGB(0, 0, 255), -1);
            cv::circle(bkup, corners[3], 3, CV_RGB(255, 255, 255), -1);
            cv::circle(bkup, center, 3, CV_RGB(255, 0, 255), -1);
            imshow("backup", bkup);
            cout << "corners size" << corners.size() << endl;
            // cv::waitKey();

             // bool sort_flag = sort_corners(corners);
             // if (!sort_flag) cout << "fail to sort" << endl;

            sortCorners(corners, center);
            cout << "corners size" << corners.size() << endl;
            cout << "tl:" << corners[0] << endl;
            cout << "tr:" << corners[1] << endl;
            cout << "br:" << corners[2] << endl;
            cout << "bl:" << corners[3] << endl;

            CalcDstSize(corners);

            cv::Mat quad = cv::Mat::zeros(g_dst_hight, g_dst_width, CV_8UC3);
            std::vector<cv::Point2f> quad_pts;
            quad_pts.push_back(cv::Point2f(0, 0));
            quad_pts.push_back(cv::Point2f(quad.cols, 0));
            quad_pts.push_back(cv::Point2f(quad.cols, quad.rows));

            quad_pts.push_back(cv::Point2f(0, quad.rows));

            cv::Mat transmtx = cv::getPerspectiveTransform(corners, quad_pts);
            cv::warpPerspective(source, quad, transmtx, quad.size());

            imshow("find", bkup);
            cv::imshow("quadrilateral", quad);

            /*如果需要二值化就解掉注释把*/
            /*
            Mat local,gray;
            cvtColor(quad, gray, COLOR_RGB2GRAY);
            int blockSize = 25;
            int constValue = 10;
            adaptiveThreshold(gray, local, 255, CV_ADAPTIVE_THRESH_MEAN_C, CV_THRESH_BINARY, blockSize, constValue);

            imshow("二值化", local);

            */


            cv::waitKey();
            return 0;
        }
    }

    cout << "can not transform!" << endl;
    waitKey();
}

11.特征检测和特征匹配

  1. SURF
    特征检测的视觉不变性是一个非常重要的概念。但是要解决尺度不变性问题,难度相当大。为解决这一问题,计算机视觉界引入了尺度不变特征的概念。 它的理念是,不仅在任何尺度下拍摄的物体都能检测到一致的关键点,而且每个被检测的特征点都对应一个尺度因子。 理想情况下,对于两幅图像中不同尺度的的同一个物体点, 计算得到的两个尺度因子之间的比率应该等于图像尺度的比率。近几年,人们提出了多种尺度不变特征,本节介绍其中的一种:SURF特征。 SURF全称为“加速稳健特征”(Speeded Up Robust Feature),我们将会看到,它们不仅是尺度不变特征,而且是具有较高计算效率的特征
    如果我们拿着这样子的匹配结果去实现图像拼接或者物体追踪,效果肯定是极差的。所以我们需要进一步筛选匹配点,来获取优秀的匹配点,这就是所谓的“去粗取精”。这里我们采用了Lowe’s算法来进一步获取优秀匹配点。为了排除因为图像遮挡和背景混乱而产生的无匹配关系的关键点,SIFT的作者Lowe提出了比较最近邻距离与次近邻距离的SIFT匹配方式:取一幅图像中的一个SIFT关键点,并找出其与另一幅图像中欧式距离最近的前两个关键点,在这两个关键点中,如果最近的距离除以次近的距离得到的比率ratio少于某个阈值T,则接受这一对匹配点。因为对于错误匹配,由于特征空间的高维性,相似的距离可能有大量其他的错误匹配,从而它的ratio值比较高。显然降低这个比例阈值T,SIFT匹配点数目会减少,但更加稳定,反之亦然。Lowe推荐ratio的阈值为0.8,但作者对大量任意存在尺度、旋转和亮度变化的两幅图片进行匹配,结果表明ratio取值在0. 4~0. 6 之间最佳,小于0. 4的很少有匹配点,大于0. 6的则存在大量错误匹配点,所以建议ratio的取值原则如下:
    ratio=0. 4:对于准确度要求高的匹配;
    ratio=0. 6:对于匹配点数目要求比较多的匹配;
    ratio=0. 5:一般情况下。
#include "highgui/highgui.hpp"    
#include <opencv2/opencv.hpp>
#include <iostream> 
#include <opencv2/xfeatures2d/nonfree.hpp>

using namespace cv;
using namespace std;

int main()
{
    Mat image01 = imread(".\\src_pic\\2.jpg", 1);    //右图
    Mat image02 = imread(".\\src_pic\\1.jpg", 1);    //左图
    namedWindow("p2", 0);
    namedWindow("p1", 0);
    imshow("p2", image01);
    imshow("p1", image02);

    //灰度图转换  
    Mat image1, image2;
    cvtColor(image01, image1, COLOR_RGB2GRAY);
    cvtColor(image02, image2, COLOR_RGB2GRAY);


    //提取特征点    
    //SurfFeatureDetector surfDetector(800);  // 海塞矩阵阈值,在这里调整精度,值越大点越少,越精准 
    Ptr<xfeatures2d::SURF> surfDetector = xfeatures2d::SURF::create(2000);
    vector<KeyPoint> keyPoint1, keyPoint2;
    surfDetector->detect(image1, keyPoint1);
    surfDetector->detect(image2, keyPoint2);

    //特征点描述,为下边的特征点匹配做准备    
    Mat imageDesc1, imageDesc2;
    surfDetector->compute(image1, keyPoint1, imageDesc1);
    surfDetector->compute(image2, keyPoint2, imageDesc2);

    //获得匹配特征点,并提取最优配对     
    FlannBasedMatcher matcher;
    vector<vector<DMatch>> matchePoints;
    vector<DMatch> GoodMatchePoints;

    vector<Mat> train_desc(1, imageDesc1);
    matcher.add(train_desc);
    matcher.train();

    matcher.knnMatch(imageDesc2, matchePoints, 2);
    cout << "total match points: " << matchePoints.size() << endl;

    //Lowe's algorithm,获取优秀匹配点
        for (int i = 0; i < matchePoints.size(); i++)
        {
            if (matchePoints[i][0].distance < 0.6 * matchePoints[i][1].distance)
            {
                GoodMatchePoints.push_back(matchePoints[i][0]);
            }
        }

        Mat first_match;
        drawMatches(image02, keyPoint2, image01, keyPoint1, GoodMatchePoints, first_match);
        imshow("first_match ", first_match);
        waitKey();
        return 0;
}
  1. SIFT
    SURF算法是SIFT算法的加速版, 而SIFT(尺度不变特征转换, ScaleInvariant Feature Transform) 是另一种著名的尺度不变特征检测法。我们知道,SURF相对于SIFT而言,特征点检测的速度有着极大的提升,所以在一些实时视频流物体匹配上有着很强的应用。而SIFT因为其巨大的特征计算量而使得特征点提取的过程异常花费时间,所以在一些注重速度的场合难有应用场景。但是SIFT相对于SURF的优点就是,由于SIFT基于浮点内核计算特征点,因此通常认为, SIFT算法检测的特征在空间和尺度上定位更加精确,所以在要求匹配极度精准且不考虑匹配速度的场合可以考虑使用SIFT算法
  2. ORB
    ORB是ORiented Brief的简称,是brief算法的改进版。ORB算法比SIFT算法快100倍,比SURF算法快10倍。在计算机视觉领域有种说法,ORB算法的综合性能在各种测评里较其他特征提取算法是最好的。ORB算法是brief算法的改进,那么我们先说一下brief算法有什么去缺点。BRIEF的优点在于其速度,其缺点是:不具备旋转不变性,对噪声敏感,不具备尺度不变性
    而ORB算法就是试图解决上述缺点中1和2提出的一种新概念。值得注意的是,ORB没有解决尺度不变性。
  3. FAST
    FAST(加速分割测试获得特征, Features from Accelerated Segment Test) 。这种算子专门用来快速检测兴趣点, 只需要对比几个像素,就可以判断是否为关键点。跟Harris检测器的情况一样, FAST算法源于对构成角点的定义。FAST对角点的定义基于候选特征点周围的图像强度值。 以某个点为中心作一个圆, 根据圆上的像素值判断该点是否为关键点。 如果存在这样一段圆弧, 它的连续长度超过周长的3/4, 并且它上面所有像素的强度值都与圆心的强度值明显不同(全部更黑或更亮),那么就认定这是一个关键点。
    用这个算法检测兴趣点的速度非常快, 因此十分适合需要优先考虑速度的应用。 这些应用包括实时视觉跟踪、 目标识别等, 它们需要在实
    时视频流中跟踪或匹配多个点。我们使用FastFeatureDetector 进行特征点提取,因为opencv没有提供fast专用的描述子提取器,所以我们借用SiftDescriptorExtractor 来实现描述子的提取。

12.图像拼接和图像融合技术

13.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值