前言
在图像处理中经常有这样的函数,在计算每个函数的像素数值时,会使用周围的像素。如果用到上下两行的像素时,就需要同时扫描上下两行的像素。下面将学习怎样实现这种扫描与计算方式。
示例
我们已图像锐化为例,进行实现。在图像处理中,有一个众所周知的结论,如果从图像中减去拉普拉斯算子部分,图像的边缘就会放大,即图像会变的更加尖锐。
下面是一种锐化的计算方式:
shaeprnedPixel = 5*current - left - right - up -down;
实现
void sharpen(cv::Mat &image, cv::Mat &result)
{
result.Create(image.size(), image.type());
int nchannels = image.channels();
for (int i = 1; i < image.rows - 1; i++)
{
const uchar* previous = image.ptr<const uchar>(i-1); //前一行
const uchar* current = image.ptr<const uchar>(i); //当前行
const uchar* next = image.ptr<const uchar>(i+1); //下一行
uchar* output = result.ptr<uchar>(i); //输出行
for (int j = 1; j < (image.cols - 1) * nchannels; j++)
{
*output++ = cv::saturate_cast<uchar>(
5 * current[j] -
current[j - nchannels] -
current[j + nchannels] -
previous[j] -
next[j]);
}
}
//将输出的边缘处补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));
}
还有一种方式是,opencv中的filter2D,使用方式如下
void sharpen2(cv::Mat &image, cv::Mat &result)
{
//构造内核
cv::Mat kernel(3, 3, CV_32F, cv::Scalar(0));
//对内核复制
kernel.at<float>(1, 1) = 5.0;
kernel.at<float>(0, 1) = -1.0;
kernel.at<float>(2, 1) = -1.0;
kernel.at<float>(1, 0) = -1.0;
kernel.at<float>(1, 2) = -1.0;
//对图像滤波
cv::filter2D(image, result, image.depth(), kernel);
}
这种方式实现的结果与上面完全相同。使用大内核的filter2D函数是特别有利的,因为这时他使用了更高效的算法。
运行
#include <opencv2/core.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
#include <random>
#include <iostream>
void sharpen(cv::Mat &image, cv::Mat &result);
void sharpen2(cv::Mat &image, cv::Mat &result);
int main(int argc, char** argv)
{
cv::Mat image = cv::imread("15011.jpg");
if (image.empty())
{
std::cout << "\n Durn, couldn't read image filename " << std::endl;
return 1;
}
cv::namedWindow("Original Image");
cv::imshow("Original Image", image);
cv::Mat result;
auto startTime = std::chrono::system_clock::now();
sharpen2(image, result);
auto endTime = std::chrono::system_clock::now();
std::cout << "time:" << std::chrono::duration_cast<std::chrono::microseconds>(endTime - startTime).count() << std::endl;
cv::namedWindow("Sharpen Image");
cv::imshow("Sharpen Image", result);
cv::waitKey(0);
return 0;
}
void sharpen(cv::Mat &image, cv::Mat &result)
{
result.create(image.size(), image.type());
int nchannels = image.channels();
for (int i = 1; i < image.rows - 1; i++)
{
const uchar* previous = image.ptr<const uchar>(i - 1); //前一行
const uchar* current = image.ptr<const uchar>(i); //当前行
const uchar* next = image.ptr<const uchar>(i + 1); //下一行
uchar* output = result.ptr<uchar>(i); //输出行
for (int j = nchannels; j < (image.cols - 1) * nchannels; j++) //注意如果是彩色图像,要从nchannels处开始
{
*output++ = cv::saturate_cast<uchar>(
5 * current[j] -
current[j - nchannels] -
current[j + nchannels] -
previous[j] -
next[j]);
}
}
//将输出的边缘处补0,很多时候花费时间去处理边缘像素是没有意义的
result.row(0).setTo(cv::Scalar(0));
result.row(result.rows - 1).setTo(cv::Scalar(0));
result.col(0).setTo(cv::Scalar(0));
result.col(result.cols - 1).setTo(cv::Scalar(0));
}
void sharpen2(cv::Mat &image, cv::Mat &result)
{
//构造内核
cv::Mat kernel(3, 3, CV_32F, cv::Scalar(0));
//对内核复制
kernel.at<float>(1, 1) = 5.0;
kernel.at<float>(0, 1) = -1.0;
kernel.at<float>(2, 1) = -1.0;
kernel.at<float>(1, 0) = -1.0;
kernel.at<float>(1, 2) = -1.0;
//对图像滤波
cv::filter2D(image, result, image.depth(), kernel);
}
经测试,第一种方式debug模式下用时12933us,第二种方式debug模式下运行2152us,可以看出在小内核下,opencv自有的filter2D也更高效。