矩阵的掩膜操作
图像的通道
图像的通道就是把图像分解成一个或多个颜色成分,常见的有单通道,三通道和四通道
-
单通道:灰度图像,一个像素点只需要一个数值即可表示,0-255(0为黑色,255为白色)
-
三通道:RGB图像,为红绿蓝三种颜色通道的叠加,三个通道缺一不可。每个通道由0-255的数值表示,当使用矩阵表示三个通道时,三个通道是并列表示的,所以rgb的图像矩阵列数为单个通道的列数乘以通道数。
-
四通道:在三通道的基础上加上了透明度(0是完全透明,255是完全不透明)
掩膜
1.掩膜的定义
掩模(Mask)是由0和1组成的一个二进制图像。当在某一功能中应用掩模时,1值区域被处理,被屏蔽的0值区域不被包括在计算中。通过指定的数据值、数据范围、有限或无限值、感兴趣区和注释文件来定义图像掩模,也可以应用上述选项的任意组合作为输入来建立掩模。
建立掩膜后,通过图像与掩膜相乘得到的图像就是掩膜操作后的图像
通过掩膜可以屏蔽某些不需要的区域,或者对某些区域的图像数值进行特殊运算,
实际上就是利用掩膜矩阵对原来的图像矩阵重新进行运算
2.利用掩膜提高图像的对比度
下面这个公式表示用5倍当前像素的值减去该像素上、下、左、右四个像素值和,得到的结果赋值给当前像素。
使用该公式可以用于提升图像的对比度。调节I(i,j)的系数权重可以得到不同的对比度提升效果。
通过这种掩膜操作就可以提高图像的对比度
opencv实现掩膜操作
1.opencv对图像像素的处理
用到的函数
(1)、CV_Assert(myImage.depth() == CV_8U)
CV_Assert()函数判断括号中是否为真,当括号中的结果为false时,返回一个错误信息,这里是为了保证输入的图像是uchar类型的。
(2)、Mat.ptr(int i=0)
获取图像矩阵的指针,其中小括号中的i表示的是图像矩阵的第几行,从0开始计数。
(3)、saturate_cast()处理像素值的范围
确保RGB图像的像素值在0-255之间
saturate_cast(-100),返回0
saturate_cast(288),返回255
saturate_cast(100),返回100
其实就是一个类型转换函数,将圆弧括号中的类型转换成尖括号中的类型。
(4)、setTo()
opencv中的setTo函数是将指定的元素设置为指定的值
例如:
1、有一个Mat src,想将他的值全部设置成0,则可以src.setTo(0)
2、setTo还有更为高级的用法,对于一个已知的src,我们要将其中大于或者小于某个值的像素值设置为指定的值,则可以如下:
src.setTo(0,src < 10)
;这句话的意思是,当src中的某个像素值小于10的时候,就将该值设置成0.
(5)、运行时间函数
若想要获得运行某段程序所花费的时间,opencv提供了两个函数来获取,分别是
getTickCount()
与
getTickFrequency()
函数
其中getTickCount()函数表示的是系统运行开始后进行的计时周期数
getTickFrequency()函数表示的是cpu的频率
而具体的时间
t=getTickCount()*getTickFrequency()
{周期数乘以频率}
代码实现(带注释)
CV_Assert(myImage.depth() == CV_8U); // CV_Assert()函数对括号内的内容进行判断,若为false,则返回错误信息
//这里要确保输入的图像是uchar类型的
const int nChannels = myImage.channels(); //图像的通道数
Result.create(myImage.size(), myImage.type()); //创建一个图像,第一个参数是图像的大小(和原图像一致),第二个参数是图像矩阵类型,和原图像一样
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); //output指针在第j行行头
for (int i = nChannels; i < nChannels*(myImage.cols - 1); ++i) //列扫描,从i=nChannels(第二组)开始
{
*output++ = saturate_cast<uchar>(5 * current[i]
- current[i - nChannels] - current[i + nChannels] - previous[i] - next[i]); //对输出图像处理,从第j行第i列开始加
}
2、函数调用-filter2D功能
(1)定义掩膜矩阵
Mat kernel = (Mat_<char>(3, 3) << 0, -1, 0,
-1, 5, -1,
0, -1, 0); //定义掩膜矩阵
这里定义的矩阵就是进行掩膜操作的运算矩阵
(2)调用filter2D()函数
filter2D函数括号内包括4个参数
例如
filter2D(src, dst1, src.depth(), kernel);
其中第一个和第二个参数表示的是输入图像和输出图像
第三个参数表示的是位图深度,有32、24、8等
第四个参数表示的是掩膜矩阵,就是上面定义的kernel
完整代码(详细注释)
#include <opencv2/imgcodecs.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
#include <iostream>
using namespace std;
using namespace cv;
static void help(char* progName)
{
cout << endl
<< "This program shows how to filter images with mask: the write it yourself and the"
<< "filter2d way. " << endl
<< "Usage:" << endl
<< progName << " [image_path -- default lena.jpg] [G -- grayscale] " << endl << endl;
}
void Sharpen(const Mat& myImage, Mat& Result);
int main(int argc, char* argv[])
{
help(argv[0]);
Mat src, dst0, dst1;
src = imread("A:/opencvproject/pikaqiu.jpg", IMREAD_COLOR);
if (src.empty()) //判断图像是否存在
{
cerr << "Can't open image"<< endl;
return EXIT_FAILURE;
}
namedWindow("Input", WINDOW_AUTOSIZE); //创建窗口input(输入图像的窗口)
namedWindow("Output", WINDOW_AUTOSIZE); //创建窗口output(输出图像的窗口)
imshow("Input", src); //显示输入图像src
//第一种方式,具体实现在Sharpen()函数中
double t = (double)getTickCount(); //开始记录时间
Sharpen(src, dst0);
t = ((double)getTickCount() - t) / getTickFrequency(); //记录转换完成时间-转换开始时间
cout << "Hand written function time passed in seconds: " << t << endl; //打印转换所用时间
imshow("Output", dst0); //显示输出图像dst0
waitKey(); //等待键盘输入
//下面是第二种方式,使用filter2D();
Mat kernel = (Mat_<char>(3, 3) << 0, -1, 0,
-1, 5, -1,
0, -1, 0); //定义掩膜矩阵
t = (double)getTickCount(); //记录第二种时间的开始时间
filter2D(src, dst1, src.depth(), kernel); //使用filter2D()函数实现图像掩膜操作
t = ((double)getTickCount() - t) / getTickFrequency(); //记录图像转换所用时间
cout << "Built-in filter2D time passed in seconds: " << t << endl; //打印使用filter2D函数所用的时间
imshow("Output", dst1); //展示第二种方式输出的图像dst1
waitKey(); //等待键盘输入
return EXIT_SUCCESS;
}
//第一种转换方式的实现函数
void Sharpen(const Mat& myImage, Mat& Result)
{
CV_Assert(myImage.depth() == CV_8U); // CV_Assert()函数对括号内的内容进行判断,若为false,则返回错误信息
//这里要确保输入的图像是uchar类型的
const int nChannels = myImage.channels(); //图像的通道数
Result.create(myImage.size(), myImage.type()); //创建一个图像,第一个参数是图像的大小(和原图像一致),第二个参数是图像矩阵类型,和原图像一样
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); //output指针在第j行行头
for (int i = nChannels; i < nChannels*(myImage.cols - 1); ++i) //列扫描,从i=nChannels(第二组)开始
{
*output++ = saturate_cast<uchar>(5 * current[i]
- current[i - nChannels] - current[i + nChannels] - previous[i] - next[i]); //对输出图像处理,从第j行第i列开始加
}
}
// 使用这种方式最外面一圈无法进行矩阵运算
Result.row(0).setTo(Scalar(0)); //第0行矩阵值设置为0
Result.row(Result.rows - 1).setTo(Scalar(0)); //最后一行矩阵之设置为0
Result.col(0).setTo(Scalar(0)); //第0列矩阵值设置为0
Result.col(Result.cols - 1).setTo(Scalar(0)); //最后一行矩阵值设置为0
}