C++ Opencv Mat类型使用的几个注意事项及自写函数实现Laplace图像锐化

为了提升自己对Opencv中Mat数据类型的熟悉和掌握程度,自己尝试着写了一下Laplace图像锐化函数,一路坎坷,踩坑不断。现将代码分享如下:

#include <opencv2/opencv.hpp>
#include <iostream>

using namespace std;
using namespace cv;

//Laplace滤波锐化图像
void myLaplace(Mat Src, Mat Tem, Mat Dst)
{
	int SrcH = Src.rows;
	int SrcW = Src.cols;
	int TemH = Tem.rows;
	int TemW = Tem.cols;

	//检测模板行列是否为奇数
	if (TemH * TemW % 2 == 0)
	{
		cerr << "模板规格错误" << endl;
		return;
	}
	
	//用于存储中间过程的计算结果。在进行滤波变换时,会有少量的行列遍历不到,为避免未遍历到的行列对结果的影响,因此将整个矩阵初始化为0,
	Mat IntDst(SrcH, SrcW, CV_32SC1, Scalar(0));

	//计算锐化后的掩模
	char* pTem = (char*)Tem.data;//Mat.data默认指针类型为 uchar*,在不同应用场合下要进行相应的类型转换
	for (int i = 0; i < SrcH - 2; i++)
	{   //Mat的各行都是连续存储的,但行与行之间不一定定是连续的,最好用哪行就取出对应行的首地址
		int* pSrc1 = Src.ptr<int>(i);
		int* pSrc2 = Src.ptr<int>(i +1);
		int* pSrc3 = Src.ptr<int>(i + 2);
		int* pIntDst = IntDst.ptr<int>(i + 1);
		for (int j = 0; j < SrcW - 2; j++)
		{
			//pSrc1[ j ]为当前模板作用邻域左上角地址
			pIntDst[ j + 1 ] = pSrc1[ j ] * pTem[ 0 ] + pSrc1[ j + 1 ] * pTem[ 1 ] + pSrc1[ j + 2 ] * pTem[ 2 ]\
				+ pSrc2[ j ] * pTem[ 3 ] + pSrc2[  j + 1 ] * pTem[ 4 ] + pSrc2[  j + 2 ] * pTem[ 5 ]\
				+ pSrc3[ j ] * pTem[ 6 ] + pSrc3[  j + 1 ] * pTem[ 7 ] + pSrc3[  j + 2 ] * pTem[ 8 ];
		}
	}

	//将滤波处理后的信息加到原图上
	addWeighted(IntDst, 1, Src, 1, 0.0, IntDst);

	//求最小值,将基准拉到0
	double minNum, maxNum;
	Point minLoc, maxLoc;
	minMaxLoc(IntDst, &minNum, &maxNum, &minLoc, &maxLoc);
	minNum = (int)minNum;

	for (int i = 0; i < SrcH; i++)
	{
		int* pIntDst = IntDst.ptr<int>(i);
		for (int j = 0; j < SrcW; j++)
		{
			pIntDst[ j ] -= minNum;
		}
	}

	//求最大值,将整体范围标定至0--255
	double newMinNum, newMaxNum;
	Point newMinLoc, newMaxLoc;
	minMaxLoc(IntDst, &newMinNum, &newMaxNum, &newMinLoc, &newMaxLoc);
	newMaxNum = (int)newMaxNum;

	for (int i = 0; i < SrcH; i++)
	{
		int* pIntDst = IntDst.ptr<int>(i);
		uchar* pDst = Dst.ptr<uchar>(i);
		for (int j = 0; j < SrcW; j++)
		{
			pIntDst[ j ] = pIntDst[ j ] * 255 / newMaxNum;
			pDst[ j ] = (uchar) pIntDst[ j ];
		}
	}
}

//将uchar型Mat矩阵写入int型Mat矩阵(后续计算像素值会超过0--255范围)
void UChar2Int(Mat inputMat, Mat outputMat)
{
	for (int i = 0; i < inputMat.rows; i++)
	{
		uchar* pInputMat = inputMat.ptr<uchar>(i);
		int* pOutputMat = outputMat.ptr<int>(i);
		for (int j = 0; j < inputMat.cols; j++)
		{
			pOutputMat[j] = (int)pInputMat[j];
		}
	}
}

int main()
{
	Mat mColorImage = imread("color.jpg");
	Mat mImage = imread("color.jpg", 0);//读取灰度图
	if (mColorImage.data == 0)
	{
		cerr << "彩图读取错误" << endl;
		return -1;
	}

	if (mImage.data == 0)
	{
		cerr << "灰图读取错误" << endl;
		return -1;
	}

	//创建3X3 Laplace算子
	char templateArray[3][3] = { {-1, -1, -1}, {-1, 8, -1}, {-1, -1, -1} };
	Mat mTemplate(3, 3, CV_8SC1, templateArray);

	//创建盛放输入信息的Mat矩阵
	Mat mIntImage(mImage.rows, mImage.cols, CV_32SC1, Scalar(0));
	UChar2Int(mImage, mIntImage);

	//创建盛放输出信息的Mat矩阵(输出灰度范围在0--255间,一定要存储在uchar中。若存放在int中,显示时默认共有2的32次方个灰度级,0--255范围过窄且靠近0,显示黑屏)
	Mat mOutputImage(mImage.rows, mImage.cols, CV_8UC1, Scalar(0));


	//进行Laplace锐化并显示
	myLaplace(mIntImage, mTemplate, mOutputImage);
	namedWindow("彩图", WINDOW_NORMAL);
	imshow("彩图", mColorImage);
	namedWindow("灰图", WINDOW_NORMAL);
	imshow("灰图", mImage);
	namedWindow("Laplace锐化", WINDOW_NORMAL);
	imshow("Laplace锐化", mOutputImage);
	waitKey();
	destroyAllWindows();

	return 0;
}

对图像进行Laplace锐化时,最令人头痛的就是数据类型的转换了。众所周知,一般的灰度图256个灰度级,在Mat中存储的数据类型都是uchar,即CV_8UC1,但进行线性运算后,矩阵中的部分数值会小于0,也有部分数值会大于255,就超出了uchar能表示的极限范围。此时就要用int,即CV_32SC1, Mat矩阵数据在两种类型之间转换时麻烦且容易出错。现将本次踩的坑与收获经验分享如下,若能助人,不胜荣幸:

1.在创建并初始化Mat时,发现了一种直接用数组初始化Mat矩阵的方法,前提是数组和矩阵大小相同且元素数据类型保持一致。

	char templateArray[3][3] = { {-1, -1, -1}, {-1, 8, -1}, {-1, -1, -1} };
	Mat mTemplate(3, 3, CV_8SC1, templateArray);

2.用Mat.data获取到的指针类型默认为uchar*型的,而与矩阵中元素的数据类型无关。使用时要注意指针类型的转化。

3.灰度图Mat矩阵中的元素多数是uchar(CV_8UC1)型的,有时需要访问其中的单个元素(像素值)并用"cout<<"输出。需要注意的是,用"cout<<"输出char/uchar型数据时,输出的并不是数字数据,而是数字对应的ASCII码字符,若对应的字符不可打印,则显示输出为空。若要求输出数字数据,可使用类型强制转换后输出(如:cout<<(int)num<<endl;)。

4.Mat的各行数据在内存中都是连续存储的,但行与行之间的地址不一定连续。因此需要用哪行的数据,就最好先获得对应行的首地址(uchar* p = image.ptr<uchar>(i),获取第i行首地址)。(在一篇博客上看到的,真伪待考证,不过谨慎点总是好的)。

5.用imshow()显示Mat矩阵存储的图像信息时,若元素的数据类型是uchar(CV_8UC1)的,就默认有256(2的8次方)个灰度级;若元素的数据类型是用int(CV_32SC1)的,就默认有2147483647 (2的32次方)个灰度级。普通灰度图的灰度值都在0-255之间,在CV_8UC1下能够正常显示。要是将其数据类型转化为CV_32SC1的,0-255的灰度值在2147483647的尺度下就显得范围过窄且无限靠近于0,用imshow()显示时显示窗口就会一片黑暗。

注:错误之处,敬请雅正!

猜你喜欢

转载自blog.csdn.net/BaiYu_King/article/details/81135061
今日推荐