OpenCV 【十三】矩阵的掩码操作
目錄
?
1 Mask掩膜/濾波核
1.1 原理
1.2 實例
1.3 結果對比
2. filter2D函數
2.1 原理
2.2 實例
2.3 結果
1 Mask掩膜/濾波核
1.1 原理
矩陣的掩碼操作很簡單。其思想是:根據掩碼矩陣(也稱作核)重新計算圖像中每個像素的值。掩碼矩陣中的值表示近鄰像素值(包括該像素自身的值)對新像素值有多大影響。從數學觀點看,我們用自己設置的權值,對像素鄰域內的值做了個加權平均。
思考一下圖像對比度增強的問題。我們可以對圖像的每個像素應用下面的公式:
上面那種表達法是公式的形式,而下面那種是以掩碼矩陣表示的緊湊形式。使用掩碼矩陣的時候,我們先把矩陣中心的元素(上面的例子中是(0,0)位置的元素,也就是5)對齊到要計算的目標像素上,再把鄰域像素值和相應的矩陣元素值的乘積加起來。雖然這兩種形式是完全等價的,但在大矩陣情況下,下面的形式看起來會清楚得多。
我們創建了一個與輸入有著相同大小和類型的輸出圖像。根據圖像的通道數,我們有一個或多個子列。我們用指針在每一個通道上迭代,因此通道數就決定了需計算的元素總數。
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)); // 右邊界
函數如下實例中Sharpen所示。
1.2 實例
#include <iostream>
#include <opencv2/core/core.hpp>
#include <opencv2/features2d/features2d.hpp>
#include <opencv2/highgui/highgui.hpp>
using namespace std;
using namespace cv;
?
void Sharpen(const Mat& myImage, Mat& Result);
?
int main(int argc, char* argv[])
{Mat I, J;I = imread("C:\\code\\sdk_for_thinmanmini\\data\\rgb.jpg");int times = 1;double t = (double)getTickCount();
?for (int i = 0; i < times; ++i)Sharpen(I, J);
?t = 1000 * ((double)getTickCount() - t) / getTickFrequency();t /= times;
?cv::imwrite("C:\\code\\sdk_for_thinmanmini\\data\\rgb2.jpg", J);cout << "Time of Sharpen (averaged for "<< times << " runs): " << t << " milliseconds." << endl;
?getchar();return 0;
}
?
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));
}
?
1.3 結果對比
可以看出,實驗后的結果明顯細節更加的突出
?
2. filter2D函數
2.1 原理
濾波器在圖像處理中的應用太廣泛了,因此OpenCV也有個用到了濾波器掩碼(某些場合也稱作核)的函數。不過想使用這個函數,你必須先定義一個表示掩碼的 Mat 對象:
Mat kern = (Mat_<char>(3,3) << 0, -1, 0,-1, 5, -1,0, -1, 0);
然后調用 filter2D 函數,參數包括輸入、輸出圖像以及用到的核:
filter2D(I, K, I.depth(), kern );
它還帶有第五個可選參數——指定核的中心,和第六個可選參數——指定函數在未定義區域(邊界)的行為。使用該函數有一些優點,如代碼更加清晰簡潔、通常比 自己實現的方法 速度更快(因為有一些專門針對它實現的優化技術)等等。
?
2.2 實例
#include <opencv2/imgproc.hpp>Mat I, J;I = imread("C:\\code\\sdk_for_thinmanmini\\data\\rgb.jpg");//![filter2D]filter2D(I, J, I.depth(), kernel);//![filter2D]t = ((double)getTickCount() - t) / getTickFrequency();cout << "Built-in filter2D time passed in seconds: ? ? " << t << endl;
?imshow("Output", J);
?waitKey();return EXIT_SUCCESS;
?
2.3 結果
總結
以上是生活随笔為你收集整理的OpenCV 【十三】矩阵的掩码操作的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 猪脚饭正宗配方的做法
- 下一篇: 《梦仙》第四十二句是什么