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()顯示時顯示窗口就會一片黑暗。

 

注:錯誤之處,敬請雅正!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM