OpenCV坐标体系+minMaxLoc的使用细节

OpenCV坐标体系+minMaxLoc的使用细节


  相信很多朋友在使用OpenCV的时候会遇到一个小问题,且有时候对这样的小问题没有引起足够的重视,或者通过表面想当然的去编程,所以调试代码时出现一些莫名其妙的问题,最后发现问题时时间已经过去了一大把。最近我在调试一个项目时就遇到过这种情况,即Mat::at(x,y)和Mat::at(Point(x, y))的区别,我在项目中把这2种看成效果一样的,结果这个问题调试时纠结了2天(因为该工程有关OpenCV的代码有上千行,且没有怀疑这两者的区别,因此有时候出现一些莫名其妙的结果,花了很多时间才找问题所在)。

  开发环境:OpenCV2.4.3+QtCreator2.5.1

 

  实验基础

 minMaxLoc函数的定义:

void minMaxLoc( const Mat& src, double* minVal, double* maxVal=0, Point* minLoc=0, Point* maxLoc=0, const Mat& mask=Mat() ); 


void minMaxLoc(const MatND& src, double* minVal, double* maxVal, int* minIdx=0, int* maxIdx=0, const MatND& mask=MatND() ); 


void minMaxLoc(const SparseMat& src, double* minVal, double* maxVal, int* minIdx=0, int* maxIdx=0);

说明:
1 minMaxLoc寻找矩阵(一维数组当作向量,用Mat定义) 中最小值和最大值的位置.
2 参数若不需要,则置为NULL或者0,即可.
3 minMaxLoc针对Mat和MatND的重载中 ,第5个参数是可选的(optional),不使用不传递即可.


  本次实验通过一个简短的例子,主要来说明下面4个问题:

  1. 坐标体系中的零点坐标为图片的左上角,X轴为图像矩形的上面那条水平线;Y轴为图像矩形左边的那条垂直线。该坐标体系在诸如结构体Mat,Rect,Point中都是适用的。(虽然网上有学着说OpenCV中有些数据结构的坐标原点是在图片的左下角,但是我暂时还没碰到过)。

  2. 在使用image.at<TP>(x1, x2)来访问图像中点的值的时候,x1并不是图片中对应点的x轴坐标,而是图片中对应点的y坐标。因此其访问的结果其实是访问image图像中的Point(x2, x1)点,即与image.at<TP>(Point(x2, x1))效果相同。

  3. 如果所画图像是多通道的,比如说image图像的通道数时n,则使用Mat::at(x, y)时,其x的范围依旧是0到image的height,而y的取值范围则是0到image的width乘以n,因为这个时候是有n个通道,所以每个像素需要占有n列。但是如果在同样的情况下,使用Mat::at(point)来访问的话,则这时候可以不用考虑通道的个数,因为你要赋值给获取Mat::at(point)的值时,都不是一个数字,而是一个对应的n维向量。

  4. 多通道图像在使用minMaxLoc()函数是不能给出其最大最小值坐标的,因为每个像素点其实有多个坐标,所以是不会给出的。因此在编程时,这2个位置应该给NULL。

 

  实验代码及注释

  main.cpp:

复制代码
#include <iostream>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>

using namespace std;
using namespace cv;

int main()
{
    Mat image, image_3c;
    image.create(Size(256, 256), CV_8UC1);
    image_3c.create(Size(256, 256), CV_8UC3);   //3通道的图像
    image.setTo(0);
    image_3c.setTo(0);

    image.at<uchar>(10, 200) = 255; //使用at函数的地方,用的是10,200
    Point point(20, 100);
    image.at<uchar>(point) = 250;//使用at函数的地方,用的是Point(10,200)

    image_3c.at<uchar>(10, 300) = 255;
    image_3c.at<uchar>(10, 302) = 254;
    Point point_3c(20, 200);
    image_3c.at<uchar>(point_3c) = 250;

    double maxVal = 0; //最大值一定要赋初值,否则运行时会报错
    Point maxLoc;
    minMaxLoc(image, NULL, &maxVal, NULL, &maxLoc);
    cout << "单通道图像最大值: " << maxVal << endl;
    double min_3c, max_3c;
    //注意多通道在使用minMaxLoc()函数是不能给出其最大最小值坐标的,因为每个像素点其实有多个坐标,所以是不会给出的
    minMaxLoc(image_3c, &min_3c, &max_3c, NULL, NULL);
    cout << "3通道图像最大值: " << max_3c << endl;

    imshow("image", image);
    imshow("image_3c", image_3c);
    waitKey(0);
    return 0;
}
复制代码

 

  实验结果:

  单通道图像的输出结果如下所示:

  

  由上图可以看出,黑色的图像中有2个白色的点(读者可以仔细看下,由于只有1个像素点,所以需要自己找下,呵呵)的位置是不同的,因此可以证明Mat::at(x,y)和Mat::at(Point(x, y))是有区别的。

 

  3通道图像的输出结果如下所示:

  

  由上图可以看出,3通道的图像也是有2个点的,但是程序中在使用Mat::at(x, y)其y的值为300和302,这已经超出了图像的宽度256。这同时证明了实验基础中的第3点。

 

  后台输出结果如下:

  

 

  实验总结:由此可见,平时一定要注意一些细节上的东西。

 

 minMaxLoc函数的其他应用:

寻找一幅图像的匹配的模板,可以在一段视频里寻找出我们感兴趣的东西,比如条形码的识别就可能需要这样类似的一个工作提取出条形码区域(当然这样的方法并不鲁棒)。而OpenCV已经为我们集成好了相关的功能。函数为matchTemplate

所谓模板匹配就是在一幅图像中寻找和模板图像(patch)最相似的区域。该函数的功能为,在输入源图像Source image(I)中滑动框,寻找各个位置与模板图像Template image(T)的相似度,并将结果保存在结果矩阵result matrix(R)中。该矩阵的每一个点的亮度表示与模板T的匹配程度。然后可以通过函数minMaxLoc定位矩阵R中的最大值(该函数也可以确定最小值)。

匹配的方法有:

CV_TM_SQDIFF  平方差匹配法,最好的匹配为0,值越大匹配越差

CV_TM_SQDIFF_NORMED归一化平方差匹配法

CV_TM_CCORR  相关匹配法,采用乘法操作,数值越大表明匹配越好

CV_TM_CCORR_NORMED  归一化相关匹配法

CV_TM_CCOEFF  相关系数匹配法,最好的匹配为1,-1表示最差的匹配

CV_TM_CCOEFF_NORMED归一化相关系数匹配法

前面两种方法为越小的值表示越匹配,后四种方法值越大越匹配。

其实模板匹配的使用和直方图反向投影calcBackProject函数很像,只是直方图反向投影对比的是直方图,而模板匹配对比的是图像的像素值,相比较而言,直方图反向投影的匹配鲁棒性更好。

总结这个函数,感觉功能不是很强大,应用不是很广,因为只能在图像中搜索出指定的模板,如果模板是从待搜索目标中截取出来的,效果会很好,如果模板不是待搜素图像的一部分,效果就差的多了,所以该函数的使用还是有很大的局限性。



部分转载于:http://www.cnblogs.com/tornadomeet 
部分转载于:http://blog.csdn.net/yang_xian521/article/details/6942194

猜你喜欢

转载自blog.csdn.net/u010312937/article/details/80791709