Mask operations on matrices are quite simple. The idea is that we recalculate each pixels value in an image according to a mask matrix (also known as kernel). This mask holds values that will adjust how much influence neighboring pixels (and the current pixel) have on the new pixel value
蒙版操作或者核操作 ~~
The first notation is by using a formula, while the second is a compacted version of the first by using a mask. You use the mask by putting the center of the mask matrix (in the upper case noted by the zero-zero index) on the pixel you want to calculate and sum up the pixel values multiplied with the overlapped matrix values.
void Sharpen(const Mat& myImage, Mat& Result) { CV_Assert(myImage.depth() == CV_8U); // accept only uchar images 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)); }
We’ll use the plain C [] operator to access pixels. Because we need to access multiple rows at the same time we’ll acquire the pointers for each of them (a previous, a current and a next line). We need another pointer to where we’re going to save the calculation.
Border
On the borders of the image the upper notation results inexistent pixel locations (like minus one - minus one). In these points our formula is undefined. A simple solution is to not apply the kernel in these points and, for example, set the pixels on the borders to zeros:
Result.row(0).setTo(Scalar(0)); // The top row Result.row(Result.rows - 1).setTo(Scalar(0)); // The bottom row Result.col(0).setTo(Scalar(0)); // The left column Result.col(Result.cols - 1).setTo(Scalar(0)); // The right columnsetTo (Scalar) —— 将矩阵置为某一元素
The filter2D function
上面的方式,自己实现了整个过滤过程 —— 其实可以把公共部分代码提取出来
For this you first need to define a Mat object that holds the mask:
Mat kern = (Mat_<char>(3,3) << 0, -1, 0, -1, 5, -1, 0, -1, 0);Then call the filter2D function specifying the input, the output image and the kernell to use:
filter2D(I, K, I.depth(), kern);The function even has a fifth optional argument to specify the center of the kernel, and a sixth one for determining what to do in the regions where the operation is undefined (borders).
这种方式更加简洁,并且比硬编码的方式效率更高
滤波的方法非常常用,而且不同mask的不同效果应该熟悉
#include "stdafx.h" #include <opencv2/core/core.hpp> #include <opencv2/highgui/highgui.hpp> #include <opencv2/imgproc/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_name -- default lena.jpg] [G -- grayscale] " << endl << endl; } void Sharpen(const Mat& myImage,Mat& Result); int main( int argc, char* argv[]) { help(argv[0]); const char* filename = argc >=2 ? argv[1] : "lena.jpg"; Mat I, J, K; if (argc >= 3 && !strcmp("G", argv[2])) I = imread( filename, CV_LOAD_IMAGE_GRAYSCALE); else I = imread( filename, CV_LOAD_IMAGE_COLOR); namedWindow("Input", WINDOW_AUTOSIZE); namedWindow("Output", WINDOW_AUTOSIZE); imshow("Input", I); double t = (double)getTickCount(); Sharpen(I, J); t = ((double)getTickCount() - t)/getTickFrequency(); cout << "Hand written function times passed in seconds: " << t << endl; imshow("Output", J); waitKey(0); Mat kern = (Mat_<char>(3,3) << 0, -1, 0, -1, 5, -1, 0, -1, 0); t = (double)getTickCount(); filter2D(I, K, I.depth(), kern ); t = ((double)getTickCount() - t)/getTickFrequency(); cout << "Built-in filter2D time passed in seconds: " << t << endl; imshow("Output", K); waitKey(0); return 0; } void Sharpen(const Mat& myImage,Mat& Result) { CV_Assert(myImage.depth() == CV_8U); // accept only uchar images 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); 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)); }