opencv学习笔记(三)– 矩阵的掩膜操作

  • Post author:
  • Post category:其他




图像的通道

图像的通道就是把图像分解成一个或多个颜色成分,常见的有单通道,三通道和四通道

  1. 单通道:灰度图像,一个像素点只需要一个数值即可表示,0-255(0为黑色,255为白色)

  2. 三通道:RGB图像,为红绿蓝三种颜色通道的叠加,三个通道缺一不可。每个通道由0-255的数值表示,当使用矩阵表示三个通道时,三个通道是并列表示的,所以rgb的图像矩阵列数为单个通道的列数乘以通道数。
    在这里插入图片描述

  3. 四通道:在三通道的基础上加上了透明度(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
}



版权声明:本文为milkhoko原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。