2.10 和OpenCV的互用性
目标
对于OpenCV的开发团队来说,不断提升OpenCV库是非常重要的。我们不断考虑那些可以减轻你工作过程的方法,同时还要保障库的灵活性。新的C++接口就是我们为了这个而开发出来的东西。然而,向后兼容性仍然是很重要的。我们不想打碎那些你使用更早的OpenCV库写下的代码。因此,我们为了保障这个事情从而加上了一些函数。在下面的教程中,你会学习到:
1、相比于同样使用的第一版本的OpenCV库,第二版本中有了什么变化。
2、如何在图像中添加一些高斯噪点。
3、什么是查找表,为什么要用他们。
概述
你做出改变的第一步就是学习关于图像的一些新的数据结构:Mat-The Basic Image Container这里代替了老旧的CvMat 和ImpImage。使用新的方程看起来更为容易一些。你只需要记住一下几件新的事情。
OpenCV 2 接受改编。不再将所有的函数都塞进单一库中。我们有许多模块,其中每一个都包括数据结构和函数相关的某些任务。通过这个方法,如果你只需要OpenCV的一部分,你就不用调用这个库。这就意味着你需要引入那些你需要的头文件。例如:
#Include<opencv2/core/core.hpp>
#include<opencv3/imgproc/imgproc.hpp>
#include<opencv2/highgui/highgui.hpp>
所有和OpenCV相关的东西都北方到了cv名字空间(namespace)中从而防止和其他库的数据结构和函数发生名字冲突。因此你需要再任何来自于OpenCV库的东西前面添加cv::关键字或者直接添加这一行代码直接包含所有:
using namespace cv;
因为函数已经在命名空间中了,也就不需要在名字前面添加cv前缀。所有的新的可兼容函数不具有这一点,他们遵循驼峰命名法。这就意味名字的第一个字母是小写字母(除非它本身就是一个名字(人名等)像Canny)接下来的单词使用大写字母开头(像copyMakeBorder)。
现在,要记住你需要将你用的所有模块连接到你的应用程序中。在Windows环境下,你需要将使用了的DLL再次添加进所有的二进制的路径中。更多深层次的信息,如果你使用Windows,请阅读 How to build applications with OpenCV inside the Microsoft Visual Studio对于Linux的使用案例,在Using OpenCV with Eclipse(添加CDT)中有所解释。
现在,为了转换Mat 对象,你可以使用IplImage或者CvMat操作符。然而在C接口中通常使用的指针,在这里不复存在了。在C++接口中,我们更多使用Mat 对象。这种对象可以不花费任何代价只需要简单的赋值就可以转换为IplImage和CvMat类型。例如:
Mat I;
IplImager PI=I;
CvMat mI=I;
现在如果你想转换到指针的,就会有一点麻烦。编译器不再自动决定你想要什么而是你需要明确指定你的目的。也就是调用IplImage和CvMat 操作符并且得到他们的指针。为了的到指针,我们使用&标记:
Mat I;
IplImage *pI=&I,operator IplImage();
CvMat *mI= &I.opeartor CvMat();
C接口的最大的弊端就是让你自己进行内存管理。你需要知道什么时候你析构一个不再用到的内存是安全的并且确保你在程序运行完成后进行,否则你可能就会有内存泄漏的麻烦了。为了解决OpenCV中的这个问题,这里引入了一系列的智能指针。当对象不再使用时,就会被自动析构掉。使用这个被特殊声明的指针Ptr:
Ptr<IplImage> piI=&I.operator IplImage();
从C的数据结构转换到Mat 是通过将它们传入内部的构造函数完成的。例如:
Mat K(piL),L;
L=Mat(pI);
关于学习
现在你已经基本完成了混合使用C接口和C++接口的例子。你也将会在OpenCV的实例文件夹中找到源代码。为了帮助你看到两者的不同:一个是C和C++的混合,另外一个则是纯C++。如果你定义DEMO_MIXED_API_USE你就不再使用第一个。程序将色彩平面分离,然后在上面做一些改动,最后将它们融合到一起。
#include<stdio.h>
#include<iostream>
#include<opencv2/core/core.hpp>
#include<opencv2/imgproc/imgproc.hpp>
#include<opencv2/highgui/highgui.hpp>
Using namespace cv;
Using namespace std;
#define DEMO_MIXED_API_USE
int main(int argc,char**argv)
{
Const char* imagename=argc>1?argv[1]:”lena.jpg”;
}
#ifdef DEMO_MIXED_API_USE
Ptr<Iplimage> IplI=cvLoadImage(imagename);
If(IplI.empty())
{
Cerr<<”an not load image”<<imagename<<endl;
Return -1;
}
Mat I(IplI);
#else
Mat I=imread(imagename);
If(I.empty())
{
Cerr<<”can not load image”<<imagename<<endl;
Return -1;
}
#endif
在这里你能看到使用了新的结构,我们不再有指针问题,尽管使用了老旧的函数,但是最终还是将结果转换为Mat 对象
Mat I_YUV;
cvtColor(I,I_YUV,COLOR_BGR2YCrCb);
Vector<Mat> planes;
Split(I_YUV,planes);
因为我们想要将图像中的亮度成分混合,我们首先将默认RGB色彩系统转换为YUV色域然后将它分离为不同的色彩平面。这里的程序分离:在第一个示例中使用三个(C语言中的[]操作符,迭代器,读取独立元素)主要的图像遍历算法中的一个来处理每一个平面。第二个变化就是我们在图像中加入高斯噪声并且根据某些方程将它们的通道融合。
MatIterator_<uchar> it=planes[0].begin<uchar>(),it_end=planes[0].end<uchar>();
For(;it!=it_end;++it)
{
Double v=*it*1.7+rand()%21-10;
*it=saturate_case<uchar>(v*v/255);
}
For(int y=0;y<I_YUV.rows;y++)
{
Uchar* Uptr=planes[1].ptr<ujchar>(y);
For(int x=0;x<I_YUV.cols;x++)
{
Uptr[x]=saturate_cast<uchar>((Uptr[x]-128/2)+128);
Uchar&Vxy=planes[2].at<uchar>(y,x);
Vxy=saturate_cast<uchar>((Vxy-128)/2+128);
}
}
在这里你能够看到我们通过三种方式(迭代器,C指针以及独立元素进入方式)来遍历整个图像中的像素。你可以从How to scan images,lookup tables and time measurement with OpenCV教程来获得更多的深层次的描述。很容易转换老旧的函数名称。仅仅把cv前缀去掉然后使用新的Mat 数据结构。下面就是通过使用增加权重的函数的使用案例:
Mat noisyI(I.size(),.CV_8U);
Randn(noisyI,Scalar::all(128),Scalar::all(20));
GaussianBlur(noisyI,noisyI,Size(3,3),0.5,0.5);
Const double brightness_gain=0;
Const double contrast_gain=1.7;
#ifdef DEMO_MIXED_API_USE
IplImage cv_planes_0=planes[0],cv_noise=noisyI;
cvAddWeighted(&cv_lanes_0,contrast_gain,&cv_noise,1,-128+brightness_gain,&cv_planes_0);
#else
addWeighted(planes[0],contrast_gain,noisyI,1,-128+brightness_gain,planes[0]);
#endif
Const double color_scale=0.5;
Planes[1].convertTo(planes[1],planes[1].type(),color_scale,128*(1-color_scale));
Planes[2]=Mat_<uchar>(planes[2]*color_scale+128*(1-color_scale));
Planes[0]=planes[0].mul(planes[0],1./255);
你可能看到平面变量的类型是Mat。然而从Mat 转换到IplImage容易而且使用一个赋值操作就可以自动完成。
Merge(planes,I_YUV);
cvColor(I_YUV,I,CV_YCrCb2BGR);
namedWindow(“image with grain”,WINDOW_AUTOSIZE);
#ifdef DEMO_MIXED_API_USE
cvShowImage(“image with grain”.IplI);
#else
Imshow(“image with grain”,I);
新的imshow highgui函数接受Mat和IplImage两种数据结构。编译和运行程序,如果下面第一幅图像是你的输入图像,你可能的到第二章或者第三章图像作为输出。