- 访问像素值
- 用指针扫描图像
- 用迭代器扫描图像
- 编写高效的图像扫描循环
- 扫描图像并访问相邻像素
- 实现简单的图像运算
- 图像重映射
在OpenCV的C++API中,所有的类和函数都在命名空间cv内定义。访问他们的方法共有两种:
(1)在定义main函数前使用如下声明:
using namespace cv ;
(2)是根据命名空间规范给所有OpenCV的类和函数加上前缀cv::,添加前缀后,代码中OpenCV的类和函数将更容易识别。
highgui模块中国有一批能帮助我们轻松显示图像并对图像进行操作的函数。
//读取一个图像文件,并将其转换为灰度图像
image = cv::imread("puppy.bmp",CV::IMREAD_GRAYSCALE);
使用imread函数装在图像时,通过设置选项把它转化为灰度图像,有些计算机视觉算法必须使用灰度图像。在读入图像的同时进行色彩转换,可以提高运行速度并减少内存的使用。这样生成的图像由无符号字节(unsigned byte,c++中为unsigned char)构成,在OpenCV中用常量CV_8U表示。
//读取图像,并将其转换为三通道彩色图像
image = cv::imread("puppy.bmp",CV::IMREAD_COLOR);
在这样创建的图像中,每个像素有3个字节,OpenCV中用CV_8UC3表示。如果输入的图像文件是灰度图像,这三个通道的值就是相同的。如果在读入图像时采用文件本身的格式,只需把第二个参数设置为负数。
std::cout<<"this image has "<<image.channels()<<"channel(s)";
可用channels方法检查图像的通道数。
注意:当用imread打开路径置顶不完整的图像时,imread会自动采用默认目录:
如果从控制台运行程序,默认路录显然就是当前控制台的目录;
如果直接在IDE中运行程序,默认目录就是项目文件所在的目录(主函数文件所在目录);
imshow显示由整数(CV_16U表示16为无符号整数,CV_32S表示32为有符号整数)构成的图像时,图像每个像素会被除以256,以便能够与在256级灰度中显示。同样在显示由浮点数构成的图像时,指的范围会被假设为0.0(显示黑色)~1.0(显示白色)。超出这个范围的值会被显示为白色(大于1.0的值)或者黑色(小于0.0的值)。
cv::flip(image,image,1);//就地处理,对image进行水平翻转
//正数表示水平,0表示垂直,负数表示水平和垂直
回调函数:响应特定事件的时候被程序调用。为了能被程序识别,回调函数需要具备特定的签名,并且必须注册。
cv::Mat iamge(240,320,CV_8UC3,cv::Scanlar(0,0,255));//创建一个红色图像,通道次序为BGR
CV ::Mat类是用来存放图像(以及其他矩阵数据)的数据结构。
一旦没有了指定cv::Mat对象的引用,分配的内存就会被自动释放。避免了C++动态内存分配中经常发生的内存泄漏问题。实现方法是通过cv::Mat实现计数引用和浅复制。深复制可以使用copyTo方法。或者产生一个图像副本的方法clone。
把一幅图像复制到另一幅图像中,且两者的数据类型不一定相同,那就要使用convertTo方法;
图像掩码:
OpenCV中国的有些操作(copyTo)可以用来定义掩码。函数或方法通常对图像中所有的像素进行操作,通过定义掩码可以限制这些函数或方法的作用范围。掩码是一个8位图像,如果掩码中某个位置的值不为0,在这个位置上的操作就会起作用;如果为0,则不起作用。
操作像素:
椒盐噪声(salt-and-pepper noise):它随机选择一些像素,把他们的颜色替换成白色或者黑色。如果通信出错,部分像素的值在传输时丢失,就会产生这种噪声。
为了降低分析的复杂性,有时需要减少图像多种颜色的数量。一种实现方法就是把RGB空间细分到大小相等的方块中。例如,如果把每种颜色数量减少到1/8,那么颜色总数就变为了32*32*32.将旧图像中的每个颜色值划分到一个方块,该方块的中间值就是新的颜色值;新图像使用新的颜色值,颜色数就减少了。
减色算法实现:假设N是减色因子,将图像中每个像素的值除以N(这里假设使用整数除法,不保留余数)。然后将结果乘以N,得到N的倍数,并且刚好不超过院士像素值。加上N/2,就得到相邻的N倍数之间的中间值。对所有8位通道值重复这个过程,就会得到(256/N)*(256/N)*(256/N)种可能的颜色值。
其他减色算法:
data[i] = (data[i] / div)*div +div/2;
减色计算也可以使用取模运算符,可以直接得到div的倍数:
data[i] = data[i] - data[i] % div +div /2;
还可以使用位运算。如果把减色因子限定为2的指数,即div = pow(2,n),那么把像素值的前n为掩码后就能得到最接近的div的倍数。可以用简单的位操作获得掩码:
//用来截取像素值的掩码
uchar mask = 0xFF << n; //如div = 16,则mask= 0xF0
*data & = mask; //掩码
*data++ += div >>1; //加上div/2
//这里的+也可改为“按位或”运算符
锐化滤波器:
0 |
-1 |
0 |
-1 |
5 |
-1 |
0 |
-1 |
0 |
在对像素领域进行计算式,通常用一个核心矩阵来表示。这个核心矩阵展现了如何将于计算相关的像素组合起来得到预期的效果。
除非另有说明,当前像素用核心矩阵中心单元格表示。核心矩阵中的每个单元格表示相关像素的乘法系数,像素应用核心矩阵得到的结果,即这些乘积的累加。核心矩阵的大小就是邻域的大小。
根据锐化滤波器的要求,水平和垂直方向的四个相邻像素与-1相乘,当前像素与5相乘。这也是信号处理中卷积概念的基础。
- 用策略设计模式比较颜色
- 用GrabCut算法分割图像
- 转换颜色表示法
- 用色调、饱和度和亮度表示颜色
策略设计模式:
策略设计模式把算法封装进类,尽可能的将算法的复杂性隐藏在一个直观的编程接口后面,更有利于算法的部署。可以通过创建类的实例来部署算法,实例通常是在程序初始化的时候创建的。在运行构造函数时,类的实例会用默认值初始化算法的各种参数,使其立即进入可用状态。还可以用适当的方法来读写算法的参数值。
GrabCut算法:
背景/前景分割步骤:
- 首先,把所有未标记的像素临时标记为前景(cv::GC_PR_FGD)。基于当前的分类情况,算法把像素划分为多个颜色相似的组(即k个背景组和k个前景组)。
- 通过引入前景和背景像素之间的边缘,确定背景/前景的分割(通过一个优化过程来实现),在此过程中,将试图连接具有相似标记的像素,并且避免边缘出现在强度相对均匀的区域。(Graph Cuts算法可以高