在圖像處理中,對當前位置像素的相鄰像素計算新的像素值是很常見的操作,當鄰域包括圖像的前幾行和下幾行時,你就需要同時掃描圖像的若干行。下面這個例子是對圖像進行銳化,它是基於拉普拉斯算子的。衆所周知,將一幅圖像減去它經過拉普拉斯濾波之後的圖像,這幅圖像的邊緣部分得到放大,即細節部分得到銳化,這個銳化的算子計算方式如下:
Sharpened_pixel=5*current-left-right-up-down;
其中left是代表當前像素左邊緊挨着它的像素,up代表在當前像素上邊緊挨着它的像素,以此類推。
實現方法如下:
void sharpend(const Mat& image, Mat& result)
{ //如有必要則分配圖像
result.create(image.size(), image.type());
for (int j = 1; j < image.rows-1; j++)
{ // 處理除了第一行和最後一行之外的所有行
const uchar* previous = image.ptr<const uchar>(j - 1); //上一行
const uchar* current = image.ptr<const uchar>(j); // 當前行
const uchar* next = image.ptr<const uchar>(j + 1); //下一行
uchar* output = result.ptr<uchar>(j); //輸出行
for (int i = 1* image.channels(); i < (image.cols-1)*image.channels(); i++)
{
output[i] = cv::saturate_cast<uchar>
(5 * current[i] - previous[i]-current[i-3]-current[i+3]-next[i]);
}
} //將未處理的圖像設置爲0
result.row(0).setTo(cv::Scalar(0,0,0));
result.row(result.rows-1).setTo(cv::Scalar(0,0,0));
result.col(0).setTo(cv::Scalar(0,0,0));
result.col(result.cols - 1).setTo(cv::Scalar(0,0,0));
}
其中模板函數cv::saturate_cast用來對計算結果進行截斷;cv::Scalar(0,0,0)指定像素的三個通道的目標值
程序代碼如下:
#include<opencv2/opencv.hpp>
#include<opencv2/highgui/highgui.hpp>
#include<iostream>
using namespace cv;
using namespace std;
void Sharpen(const Mat& image, Mat& ret);
void sharpenByFilter2D(const Mat& image, Mat& ret);
int main()
{
Mat image;
image = imread("8.jpg");
if (!image.data)
{
return -1;
}
Mat copy;
image.copyTo(copy);
//filter2D()
namedWindow("before");
imshow("before", image);
namedWindow("Sharpen"); //銳化之後的圖像
Sharpen(image, image);
imshow("Sharpen", image);
namedWindow("sharpenByFilter2D");
sharpenByFilter2D(copy, copy); //使用cv::filter2D 進行圖像濾波,加過圖像濾波後的圖像
imshow("sharpenByFilter2D", copy);
cvWaitKey(-1);
destroyAllWindows();
return 0;
}
// 銳化算子公式: sharpened_pixel = 5 * current - left - right - up - down;
void Sharpen(const Mat& image, Mat& ret)
{
CV_Assert(image.depth() == CV_8U); // accept only uchar images
ret.create(image.size(), image.type()); // 保證和原圖像素等一樣大小
const int nChannels = image.channels();
// 除了第一行和最後一行,因爲其鄰域不完整
for (int j = 1; j < image.rows - 1; ++j)
{
const uchar* pre = image.ptr<uchar>(j - 1);
const uchar* cur = image.ptr<uchar>(j);
const uchar* next = image.ptr<uchar>(j + 1);
uchar* output = ret.ptr<uchar>(j);
// 除了第一列和最後一列,因爲其鄰域不完整
for (int i = nChannels; i < nChannels * (image.cols - 1); ++i)
{
// saturate_cast<uchr>用於確保該值在0-255之間
*output++ = saturate_cast<uchar>(5 * cur[i] - cur[i - nChannels]
- cur[i + nChannels] - pre[i] - next[i]);
}
}
// 將未處理的像素點簡單的設置爲0值
if (nChannels == 1)
{
ret.row(0).setTo(Scalar(0));
ret.row(ret.rows - 1).setTo(Scalar(0));
ret.col(0).setTo(Scalar(0));
ret.col(ret.cols - 1).setTo(Scalar(0));
}
else if (nChannels == 3)
{
ret.row(0).setTo(Scalar(0, 0, 0));
ret.row(ret.rows - 1).setTo(Scalar(0, 0, 0));
ret.col(0).setTo(Scalar(0, 0, 0));
ret.col(ret.cols - 1).setTo(Scalar(0, 0, 0));
}
}
void sharpenByFilter2D(const Mat& image, Mat& ret)
{
// 定義一個圖像濾波器(核),以使用cv::filter2D 進行圖像濾波
Mat kern = (Mat_<char>(3, 3) << 0, -1, 0,
-1, 5, -1,
0, -1, 0);
filter2D(image, ret, image.depth(), kern);
}
opencv定義了一個特殊的函數來完成濾波處理:cv::filter2D。在使用它之前,必須先以矩陣的形式定義一個核,之後以一幅圖像和這個核爲參數調用此函數,函數返回濾波後的圖像。利用這個函數,我們可以很簡單的重寫圖像銳化函數。
運行結果如下:
原始圖像:
圖像銳化處理後如下:
對圖像進行濾波處理後如下: