天天看点

c++ opencv mat_OpenCV计算机视觉-Core组件(一)

在之前我们说到,图片在计算机中存储的方式是像素存储,图像矩阵的大小取决于所用的颜色模型,也就是取决于它所用的通道数。灰度图像的矩阵如下图所示:

c++ opencv mat_OpenCV计算机视觉-Core组件(一)

对于多通道的图像来说,矩阵中的列会包含多个子列,其子列个数与通道数相等。在OpenCV中,子列的通道顺序是反过来的。很多情况下,因为内存足够大,可实现连续存储,因此,图像中的各行就能一行一行地连接起来,形成一个长行。连续存储有助于提升图像扫描速度,我们可以使用isContinuousO来判断矩阵是否是连续存储的。

Ⅰ颜色空间的缩减

在灰度图像中,如果存储的是单通道图像像素,使用C字符类型时,像素就会出现256种不同的情况。如果是更多通道的像素,使用C字符类型,就会出现几百万种甚至几千万种可能性,使用这么多颜色进行运算,会对算法造成不可逆转的伤害。

换一种思路考虑,使用这些像素中很少一部分具有代表性的颜色,就可以达到相同的效果,因此我们引入了颜色空间缩减的概念。

颜色空间缩减(color space reduction),是指将现有颜色空间值除以某个输入值,以获得较少的颜色数的过程,它在很多应用中可以大大降低运算复杂度。以uchar类型的四通道图像,每个通道取值可以是0-255,于是就有256x256x256x256个不同的值。我们可以如下定义:

c++ opencv mat_OpenCV计算机视觉-Core组件(一)

由上图所知,我们可以得到一个26x26x26x26的颜色值,由于C语言具有自动截余的功能,我们可以方便的得出这个式子。

c++ opencv mat_OpenCV计算机视觉-Core组件(一)

由于C语言具有自动截余的功能,例如I(old)=14,则:

c++ opencv mat_OpenCV计算机视觉-Core组件(一)

在处理图像像素时,每个像素都需要进行一遍上述计算,也需要一定的时间。但我们注意到其实只有0〜255种像素,即只有256种情况。进一步可以把这256种计算好的结果提前存在表中table中,这样每种情况不需计算,直接从table中取结果即可。

int divideWith=10;
uchar table[256];
for (int i - 0; i < 256; ++i)
table[i]=divideWith* (i/divideWith);
           

所以table[i]中存放的是颜色缩小后的值,这样,简单的颜色空间缩减算法就可由下面两步组成:

  1. 遍历图像矩阵的每一个像素;
  2. 对像素应用上述公式。
ⅡLUT函数和计时函数

在OpenCV视觉库中,官方推荐我们实体一个原型为operationsOnArrayslUTO^"的函数来进行。它用于批量进行图像元素査找、扫描与操作图像。它的使用方法如下:

//首先我们建立一个mat型用于畳衰
Mat lookUpTabled, 256, CV_8U);
uchar* p - lookUpTable.data;
for( int i - 0; i < 256; ++i)
p[i] - table[i];
//然后我们调用函象(I是输入J是输出):
for (int i = 0; i < times; ++i)
LOT(I, lookUpTable, J);
           

另外有个问题是如何计时。可以利用这两个简便的计时函数~getTickCountQ和 getTickFrequency()。

  • getTickCount()函数返回CPU自某个事件(如启动电脑)以来走过的时钟周期数;
  • getTickFrequency()函数返回CPU一秒钟所走的时钟周期数。这样,我们就能轻松地以秒为单位对某运算计时。
double timeO = static_cast<double>(getTickCount());//记录起给时间
//进行图像处理操作
timeO - ((double)getTickCount() - timeO)/getTickFrequency();
cout«w此方法运行时冋为:*,«timeO«"秒”<<endl; //输由延行时冋
           
Ⅲ访问图像像素的方式

任何图像处理算法,都是从操作每个像素开始的。即使我们不使用OpenCV提供的各种图像处理函数,只要了解了图像处理算法的基本原理,也可以写出具有相同功能的程序。在OpenCV中,提供了三种访问每个像素的方法。

  • 指针访问:C操作符();
  • 迭代器iterator;
  • 动态地址计算。

这三种方法在访问速度上略有差异。debug模式下,这种差异非常明显,不过在release模式下,这种差异就不太明显了。我们通过一组程序来说明这几种方法。程序的目的是减少图像中颜色的数量,比如原来的图像是是256种颜色,我们希望将它变成64种颜色,那只需要将原来的颜色除以4 (整除)以后再乘以4就可以了。

#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <iostream>
using namespace std;
using namespace cv;
//定义全局函数
void colorReduce(Matt inputImage, Mat& outputImage, int div);
//定义main函数
int main。
(
//创建原始图并甚示
Mat srclmage=imread("l.jpg");
imshow("原始图像",srclmage);
// 按原始图的M规格来创定创建效果图
Mat dstlmage;
dstlmage.create(srclmage.rows,srclmage.cols,srclmage.type());//效果图的大小、类型与原图片相同
// 记录起始酎间
double timeO=static_cast<double>(getTickCount());
//调用顔色空间编减函数
colorReduce(srclmage,dstlmage,32);
//计算运行时间并・出
timeO = ((double)getTickCount 0 - timeO)/getTickFrequency();
cout«"A方法运行时间为:"<<timeO«"^"«endl; //输出运行时何
//显示效果图
imshow ("itMH", dstlmage);
waitKey(O);
}
           

用指针访问像素的这种方法利用的是C语言中的操作符,这种方法最快,但是略有点抽象.实验条件下单次运行时间为0.00665378。

void colorReduce(Mats inputimage. Mats outputImage, int div)
{
//参数准备
outputImage - inputimage.clone(); //复制实参临时变量
int rowNumber = outputImage.rows; //行数
int colNumber = outputimage.cols*outputlmage.channels ();//列通道數=每一行元素的个教
//双重循环.遍历所有的像素值
for(int i = 0;i < rowNumber;i++) //行循环
uchar* data - outputlmage.ptr<uchar>(i); //获取第一行首地址
for (int j - 0;j < colNumber; j++) //列循环
{
//开始处理每个像素
data[j]=data[j]/div*div + div/2;
} //行处理结束
           

第二种方法为用迭代器操作像素,这种方法与STL库的用法类似。在迭代法中,我们所需要做的仅仅是获得图像矩阵的begin和end,然后増加迭代直至从begin到end。将*操作符添加在迭代指针前,即可访问当前指向的内容。相比用指针直接访问可能出现越界问题,迭代器绝对是非常安全的方法。

void colorReduce(Mat& inputImage, Mat& outputimage, int div)
{
//参数准备
outputimage = input Image, clone (); //复制实参到临时变量
//获取迭代器
Mat_<Vec3b>:: iterator it=output Image. begin<Vec3b> (); 〃初始位的迭代
Mat_<Vec3b>:: iterator itend - outputImage.end<Vec3b> (); 〃终止迭代
//存取彩色图像像素
for(;it !- itend;++it)
//开始处理每个像素
(*it) [0] = (*it)(0)/div*div + div/2;
(*it) [1] = (*it)[l]/div*div + div/2;
(*it)[2] = (*it)[2]/div*div + div/2;
//处理结束
           

第三种方法为用动态地址计算来操作像素。下面是使用动态地址运算配合at方法的colorReduce函数的代码。这种方法简洁明了,符合大家对像素的直观认识。

void colorReduce(Mats inputImage, Mat& outputImage, int div)
{
//参数准备
output Image = inputimage. clone (); //复制实参到临时变量
int rowNumber = output Image. rows; //行数
int colNumber = outputimage.cols;//列数
//存取彩色图像像素
for(int i - 0;i < rowNumber;i++)
(
for(int j - 0;j < colNumber;j++)
{
//开始处理毎个像素
outputImage.at<Vec3b>(i,j)[0]=
output Image. a t<Vec3b> (i, j) [ 0 ] /di v*div + div/2; //蓝色通道
outputImage.at<Vec3b>(i,j)[1]=
outputImage.at<Vec3b>(i, j) [1] /div*div + div/2; 〃缘色通道
outputImage.at<Vec3b>(i,j)[2]=
outputlraage.at<Vec3b>(i,j)[2]/div*div + div/2; //红色通道
//处理结束
} //行处理结束
           

继续阅读