OpenCV介绍
这是一篇关于opencv的学习指南。
首先,opencv可以运行于linux平台、windows平台、Android平台及iOS平台。
在这里,我选择windows平台进行opencv环境的配置。
注意,要使用opencv库,您有两个选择。
一是通过使用预构建的库进行安装,或者通过源文件中创建自己的库进行安装。
我们选择了法一,需要在最新的Microsoft VIsual Studio集成开发环境(IDE)中编写代码
完成了opencv的配置,
我们开始学习opnecv的基本操作:
(1)加载头文件、命名空间
opencv有自己的名称空间:cv. 为了避免在前面添加CV:: 关键字,可以使用以下代码行导入整个文件中的名称信息。
using namespace cv;
(2)创建一个mat对象用于存储图像信息
Mat image;
1、加载图像
通过调用imread函数加载图像,函数原型如下:
image = imread(argv[1], CV_LOAD_IMAGE_COLOR);
//第一个参数表示图像加载路径(路径使用两个反斜杠),第二个参数表示指定我们想要的图像格式
第二个参数可选值,分别为以下几种:
CV_LOAD_IMAGE_UNCHANGED(<0)按原样加载图像
CV_LOAD_IMAGE_GRAYSCALE(0)图像增强加载
CV_LOAD_IMAGE_COLOR(>0)以RGB格式加载图像
(注:opencv支持windows位图bmp,可移植图像格式(pbm、pgm、ppm)和Sun光栅(sr、ras)。也可以加载图像格式JPEG(JPEG,jpg,jpe),JPEG 2000,TIFF文件(TIFF,tif)和便携式网络图形(png))
2、创建OpenCV窗口
使用namedWindow函数创建OpenCV窗口。一旦创建它们,OpenCV就会自动管理它们。为此,您需要指定它的名称,以及它应该如何从大小的角度处理它所包含的图像的更改
namedWindow( "Display window", CV_WINDOW_AUTOSIZE );// Create a window for display.
CV_WINDOW_AUTOSIZE 不允许设置窗口大小,窗口被图片填满
CV_WINDOW_NORMAL 允许设置窗口大小,包括CV_WINDOW_KEEPRATIO(锁定长宽比)和CV_WINDOW_FREERATIO(不锁定)
3、显示图像
imshow( "Display window", image );
4、图像保持在屏幕
waitKey(0); //0意味着forever
5、图像颜色空间的转换cvtColor
cvtColor( image, gray_image, CV_BGR2GRAY );//第一个参数表示原图像,第二个参数表示目标图象,第三个参数用于指定参数类型
6、图像保存至硬盘(imwrite)
imwrite( "../images/Gray_Image.jpg", gray_image );
core模块,核心功能
必须学会图像在像素层是如何被处理的
1、Mat——基本图像容器
Mat是一个类,有两个数据部分组成,矩阵头(包含矩阵尺寸,存储方法,存储地址等)和一个指向存储所有像素值的矩阵(根据所选存储方法的不同矩阵可以是不同的维数)的指针。矩阵头的尺寸是常数值,矩阵本身的尺寸一般比矩阵头的尺寸大很多。
为了避免大量的内存消耗,opencv引入了计数机制,其思路是让每个Mat对象有自己的信息头,同时共享一个矩阵,这通过让矩阵指针指向统一地址实现的。而拷贝构造函数则值好呗信息统一和矩阵指针。
创建一个感兴趣区域(RIO),你只需创建包含便捷信息的信息头
Mat E = A(Range:all(), Range(1,3))
需要拷贝矩阵本身(不只是信息头和矩阵指针),这时可以使用函数 clone() 或者 copyTo() 。
如何存储像素值,需要指定颜色空间和数据类型,alpha表示透明颜色(有时候作为第四个元素引入)
(1)显式地创建一个Mat对象
Mat M(2,2, CV_8UC3, Scalar(0,0,255));
//第一二个参数分别表示行数和列数,第三个表示指定存储元素的数据类型以及每个矩阵点的通道数,第四个Scalar是short类型的vector,制定这个可以初始化矩阵。
//CV_8UC3 表示使用8位的 unsigned char 型,每个像素由三个元素组成三通道。预先定义的通道数可以多达四个
create函数,不能设置矩阵初始值,知识改变尺寸时重新为矩阵数据开辟内存。
2、图像遍历
注意,子列的通道顺序是反过来的,是BGR不是RGB,内存大可以实现连续存储,连续存储有助于提高图像遍历速度,可以使用isontinuous()来判断矩阵是否是连续存储的。
(1)通过Effiicient way遍历
int channels = I.channels();
int nRows = I.rows * channels;
int nCols = I.cols;
if (I.isContinuous())
{
nCols *= nRows;
nRows = 1;
}
int i,j;
uchar* p;
for( i = 0; i < nRows; ++i)
{
p = I.ptr(i);
for ( j = 0; j < nCols; ++j)
{
p[j] = table[p[j]];
}
}
(2)通过On-the-fly地址计算
Mat_ _I = I;
for( int i = 0; i < I.rows; ++i)
for( int j = 0; j < I.cols; ++j )
{
_I(i,j)[0] = table[_I(i,j)[0]];
_I(i,j)[1] = table[_I(i,j)[1]];
_I(i,j)[2] = table[_I(i,j)[2]];
}
I = _I;
3、图像的掩码操作
对图像实现这个操作的代码
for(int j = 1 ; j < myImage.rows-1; ++j)
{
const uchar* previous = myImage.ptr(j - 1);
const uchar* current = myImage.ptr(j );
const uchar* next = myImage.ptr(j + 1);
uchar* output = Result.ptr(j);
for(int i= nChannels;i < nChannels*(myImage.cols-1); ++i)
{
*output++ = saturate_cast(5*current[i]
-current[i-nChannels] - current[i+nChannels] - previous[i] - next[i]);
}
}
4、图像线性混合操作(一种典型的二元像素操作)
注意:因为我们对 src1 和 src2 求 和 ,它们必须要有相同的尺寸(宽度和高度)和类型。
addWeighted( src1, alpha, src2, beta, 0.0, dst);//使用这个函数就能够实现叠加
5、改变图像的对比度和亮度
在这一类的图像处理变换中,仅仅根据输入像素值计算相应的输出像素值
/// 执行运算 new_image(i,j) = alpha*image(i,j) + beta
for( int y = 0; y < image.rows; y++ )
{
for( int x = 0; x < image.cols; x++ )
{
for( int c = 0; c < 3; c++ )
{
new_image.at(y,x)[c] = saturate_cast( alpha*( image.at(y,x)[c] ) + beta );
}
}
}
6、基本绘图
大量使用Point和Scalar这两个结构
Point pt = Point(10, 8);//pt.x,pt.y