OpenCV学习笔记:矩阵的掩码操作
矩陣的掩碼操作很簡單。其思想是:根據掩碼矩陣(也稱作核)重新計算圖像中每個像素的值。掩碼矩陣中的值表示近鄰像素值(包括該像素自身的值)對新像素值有多大影響。從數學觀點看,我們用自己設置的權值,對像素鄰域內的值做了個加權平均。
?測試用例
思考一下圖像對比度增強的問題。我們可以對圖像的每個像素應用下面的公式:
上面那種表達法是公式的形式,而下面那種是以掩碼矩陣表示的緊湊形式。使用掩碼矩陣的時候,我們先把矩陣中心的元素(上面的例子中是(0,0)位置的元素,也就是5)對齊到要計算的目標像素上,再把鄰域像素值和相應的矩陣元素值的乘積加起來。雖然這兩種形式是完全等價的,但在大矩陣情況下,下面的形式看起來會清楚得多。
現在,我們來看看實現掩碼操作的兩種方法。一種方法是用基本的像素訪問方法,另一種方法是用?filter2D?函數。
?基本方法
下面是實現了上述功能的函數:
void Sharpen(const Mat& myImage,Mat& Result) {CV_Assert(myImage.depth() == CV_8U); // 僅接受uchar圖像Result.create(myImage.size(),myImage.type());const int nChannels = myImage.channels();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]);}}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?函數。若該函數括號內的表達式為false,則會拋出一個錯誤。
CV_Assert(myImage.depth() == CV_8U); // 僅接受uchar圖像然后,我們創建了一個與輸入有著相同大小和類型的輸出圖像。在?圖像矩陣是如何存儲在內存之中的??一節可以看到,根據圖像的通道數,我們有一個或多個子列。我們用指針在每一個通道上迭代,因此通道數就決定了需計算的元素總數。
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]);} }在圖像的邊界上,上面給出的公式會訪問不存在的像素位置(比如(0,-1))。因此我們的公式對邊界點來說是未定義的。一種簡單的解決方法,是不對這些邊界點使用掩碼,而直接把它們設為0:
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也有個用到了濾波器掩碼(某些場合也稱作核)的函數。不過想使用這個函數,你必須先定義一個表示掩碼的?Mat?對象:
Mat kern = (Mat_<char>(3,3) << 0, -1, 0,-1, 5, -1,0, -1, 0);然后調用?filter2D?函數,參數包括輸入、輸出圖像以及用到的核:
filter2D(I, K, I.depth(), kern );它還帶有第五個可選參數——指定核的中心,和第六個可選參數——指定函數在未定義區域(邊界)的行為。使用該函數有一些優點,如代碼更加清晰簡潔、通常比?自己實現的方法?速度更快(因為有一些專門針對它實現的優化技術)等等。例如,我測試的濾波器方法僅花了13毫秒,而前面那樣自己實現迭代方法花了約31毫秒,二者有著不小差距。
示例:
你可以從?here?下載這個示例的源代碼,也可瀏覽OpenCV源代碼庫的示例目錄samples/cpp/tutorial_code/core/mat_mask_operations/mat_mask_operations.cpp?
轉載于:https://www.cnblogs.com/james1207/p/3266586.html
總結
以上是生活随笔為你收集整理的OpenCV学习笔记:矩阵的掩码操作的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: html怎么让方块自动旋转,如何使用纯C
- 下一篇: 乐高魔方机器人编程及图纸_乐高解魔方机器