在图像处理中,对当前位置像素的相邻像素计算新的像素值是很常见的操作,当邻域包括图像的前几行和下几行时,你就需要同时扫描图像的若干行。下面这个例子是对图像进行锐化,它是基于拉普拉斯算子的。众所周知,将一幅图像减去它经过拉普拉斯滤波之后的图像,这幅图像的边缘部分得到放大,即细节部分得到锐化,这个锐化的算子计算方式如下:

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。在使用它之前,必须先以矩阵的形式定义一个核,之后以一幅图像和这个核为参数调用此函数,函数返回滤波后的图像。利用这个函数,我们可以很简单的重写图像锐化函数。

运行结果如下:

原始图像:

opencv 边沿增强 opencv边缘锐化_图像滤波

图像锐化处理后如下:

opencv 边沿增强 opencv边缘锐化_图像滤波_02

对图像进行滤波处理后如下:

opencv 边沿增强 opencv边缘锐化_邻域_03