矩阵的蒙板操作

来源:互联网 发布:清华大学网络学堂旧版 编辑:程序博客网 时间:2024/05/22 05:21

矩阵的掩模操作是极简单的操作。其思想就是重新计算根据掩模矩阵(也被称作内核)每一个像素的值。掩模中包含了邻近点的像素值对新的像素点的值的影响(包含了这个像素本身)。从数学的角度来看,我们是在意我们指定的值做了一次加权平均。

 

我们的测试案例

让我们来考虑一个用何种方法提高图像对比度的讨论。一般我们会想到把下面这个方程式应用到图像中的每一像素:

                                   I(I,j)= 5*I(I,j)- [I(I + 1) + I(I,j-1 + (I,j+1)]

                           i\j  --1   0   --1

             I(I,j)*M,M =  

第一个的表达方式是通过使用公式,而第二个是第一个的使用蒙版的压缩版本。你在使用蒙版时要把蒙版矩阵(以zero-zero索引所指出的大写)的中心放在你需要计算的像素上并将具有多重性的像素的像素值和重叠的矩阵值累加。这两种方法是一回事,但是在遍历大型矩阵的情况下后者更容易。

现在让我们看看我们是如何通过使用基本的像素访问方法或使用 filter2D 函数使这种事情发生的。

基本方法

这就是要完成这项工作的函数:

voidSharpen(const Mat& myImage,Mat& Result)

{

CV_Assert(myImage.depth()== CV_8U); // 仅处理uchar类型图象

Result.create(myImage.size(),myImage.type());

constint nChannels = myImage.channels();

for(intj = 1 ; j < myImage.rows-1; ++j)

{

constuchar* previous = myImage.ptr<uchar>(j - 1);

constuchar* current = myImage.ptr<uchar>(j );

constuchar* next = myImage.ptr<uchar>(j + 1);

uchar*output = Result.ptr<uchar>(j);

for(inti= nChannels;i < nChannels*(myImage.cols-1); ++i)

{

*output++= saturate_cast<uchar>(5*current[i]

             -current[i-nChannels] -current[i+nChannels] - previous[i] - next[i]);

}

}

Result.row(0).setTo(Scalar(0));

Result.row(Result.rows-1).setTo(Scalar(0));

Result.col(0).setTo(Scalar(0));

Result.col(Result.cols-1).setTo(Scalar(0));

}

首先,我们必须确保输入的图像数据时无符号字符型的。为此我们用CV_Assert函数来抛出错误,当函数内的表达式出现错误为假的时候函数就会抛出错误。

CV_Assert( myImage.depth() == CV_8U);  //仅处理uchar类型的图象

我们创建了一个相同尺寸的相同类型的输出图像。正如你在How the image matrix is stored in thememory 部分,依据通图像的道数图像矩阵中至少有一个子列。我们将会使用指针的方式通过迭代处理每一列,因此,迭代次数的总数是多少就要看列数了。

Result.create(myImage.size(),myImage.type());

const int nChannels = myImage.channels();

我们将用简易的C[]操作符来访问像素。因为我们需要访问多行与此同时我们需要得到每一行的指针(前一行的,当前行的,以及下一行的)。我们还需要一个保存计算结果的指针,然后知需要简单地通过[]操作符去访问对应的项。我们只需在每次操作完成后简单地为输出指针增加一个字节来前移这个输出指针:

for(int j = 1; j < myImage.rows-1; ++j)

{

const uchar* previous = myImage.ptr<uchar>(j - 1);

const uchar* current = myImage.ptr<uchar>(j );

const uchar* next = myImage.ptr<uchar>(j + 1);

uchar* output = Result.ptr<uchar>(j);

for(int i=nChannels;i < nChannels*(myImage.cols-1); ++i)

{

*output++ =saturate_cast<uchar>(5*current[i]

-current[i-nChannels] -current[i+nChannels] - previous[i] - next[i]);

}

}

在图像的边缘,前一种表达方式会得到一个不存在的结果(像负1减负1)。在这一类的点上,方程比没有给出定义。一个简单的解决发难就是不要在这些点上使用蒙板,然后,比如说,通过复制原始图像中的原先的值保留图像的原始数据:

Result.row(0).setTo(Scalar(0)); // 顶行

Result.row(Result.rows-1).setTo(Scalar(0));//左侧的一列

Result.col(0).setTo(Scalar(0)); // 右侧一列

Result.col(Result.cols-1).setTo(Scalar(0));// 底行

filter2D函数

使用OpenCV处理图像时使用这样的过滤函数是很普遍的,OpenCV本身就有一个考虑了蒙板的(有时也被称为kernel)的过滤函数。对于这个函数,你必须先定义一个Mat对象来保存蒙板矩阵:

Mat kern = (Mat_<char>(3,3) << 0, -1, 0,

                           -1, 5, -1,

                           0, -1, 0);

然后调用filter2D函数来指定输入、输出以及用到的kernel1:

filter2D(I, K, I.depth(), kern );

该函数一共有5个可选参数来指定kernel的中心,第六个用于决定在为定义的区域(如图像边缘)作何种处理。使用这个函数的好处就是它很短,那些代码之所以更冗长适应为他们实现了一些优化技术使得它们比通常现成的方法更快(hand method)。比如我用第一种方法只要13毫秒,而第二种就需要将近31毫秒。

如图:


你可以在这里下载到这个例子的源码或是在OpenCV的源码库中的sample目录下面找 sample/cpp/tutorial_code/core/mat_mask_operations/mat_mask_operations.cpp。

你可以登录我们的YouTube频道观看实时的演示。

2 0
原创粉丝点击