在图像处理中,对当前位置像素的相邻像素计算新的像素值是很常见的操作,当邻域包括图像的前几行和下几行时,你就需要同时扫描图像的若干行。下面这个例子是对图像进行锐化,它是基于拉普拉斯算子的。众所周知,将一幅图像减去它经过拉普拉斯滤波之后的图像,这幅图像的边缘部分得到放大,即细节部分得到锐化,这个锐化的算子计算方式如下:
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。在使用它之前,必须先以矩阵的形式定义一个核,之后以一幅图像和这个核为参数调用此函数,函数返回滤波后的图像。利用这个函数,我们可以很简单的重写图像锐化函数。
运行结果如下:
原始图像:
图像锐化处理后如下:
对图像进行滤波处理后如下: