Day 9 - Opencv 扫描图像并访问相邻像素

Day 9 - Opencv 扫描图像并访问相邻像素

前言

在图像处理中经常有这样的函数,在计算每个函数的像素数值时,会使用周围的像素。如果用到上下两行的像素时,就需要同时扫描上下两行的像素。下面将学习怎样实现这种扫描与计算方式。

示例

我们已图像锐化为例,进行实现。在图像处理中,有一个众所周知的结论,如果从图像中减去拉普拉斯算子部分,图像的边缘就会放大,即图像会变的更加尖锐。
下面是一种锐化的计算方式:

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也更高效。

在这里插入图片描述

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值