学习opencv:PS滤镜—浮雕

实现浮雕效果的算子有很多,效果大同小异,不同算子的处理结果在细节上会有所差异。事实上,任何一阶差分算子都可用于实现浮雕效果,简单起见,这里使用算子[-1,1]。

代码如下

#include<iostream>  
#include <opencv2/core/core.hpp>  
#include <opencv2/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>  

using namespace std;

int main()
{
	int rows, cols, i, j, tmp;
	int shift = 128, alpha = 1;
	cv::Mat img = cv::imread("D:\\timg.jpg");
	rows = img.rows;
	cols = img.cols;
	cv::Mat result(rows, cols - 1, CV_8UC3, cv::Scalar(0, 0, 0));

	for (i = 0; i < rows ; i++)
	{
		cv::Vec3b* data1 = img.ptr<cv::Vec3b>(i);
		cv::Vec3b* data2 = result.ptr<cv::Vec3b>(i);
		for (j = 0; j < cols - 1; j++)
		{
			tmp = (-data1[j][0] + data1[j + 1][0])*alpha + shift; //alpha决定雕刻"深度"
			data2[j][0] = tmp < 0 ? 0 : tmp>255 ? 255 : tmp;      //限制像素值范围在0~255
			tmp = (-data1[j][1] + data1[j + 1][1])*alpha + shift;
			data2[j][1] = tmp < 0 ? 0 : tmp>255 ? 255 : tmp;
			tmp = (-data1[j][2] + data1[j + 1][2])*alpha + shift;
			data2[j][2] = tmp < 0 ? 0 : tmp>255 ? 255 : tmp;
		}
	}
	cv::imshow("result", result);
	cv::waitKey(10000);
	return 0;
}

注意限制像素值范围很重要,否则当像素值超出范围时,int型数据赋给uchar型数据会溢出,在结果图像中会产生杂色。

效果如下

原图:

                                                                                               图1 原图

效果图:

                                                                                         图2  浮雕效果图 

上图整体呈现灰色,具有浮雕效果的立体感。边缘有略微的彩色,若要消除彩色,可将上图转为灰度图。调整代码中的alpha值,可改变雕刻"深度",增强或减弱立体感。

原理 

浮雕效果的特征是具有一定的立体感,这是通过明暗对比实现的,观察图2,可以发现一块凸起的左右边缘总有一侧比背景暗,而另一侧比背景亮,从而形成一种光线从一侧照向另一侧的视觉效果,进而形成立体感。若消除这种明暗对比,则不会形成立体感,将代码作如下修改,对差分结果取绝对值,去除明暗对比

-  tmp = (-data1[j][0] + data1[j + 1][0])*alpha + shift;
+  tmp = cv::abs(-data1[j][0] + data1[j + 1][0])*alpha + shift;
	
-  tmp = (-data1[j][1] + data1[j + 1][1])*alpha + shift;
+  tmp = cv::abs(-data1[j][1] + data1[j + 1][1])*alpha + shift;

-  tmp = (-data1[j][2] + data1[j + 1][2])*alpha + shift;
+  tmp = cv::abs(-data1[j][2] + data1[j + 1][2])*alpha + shift;

得到如下效果

                                                                                          图3  平面效果

可见,是差分算子决定了处理结果具有立体感。图像中颜色相近区域进行差分处理后,其值接近0,加上shift后像素各通道值均接近shift,因而呈现灰色,而边缘处值有较大变化,因此可能会呈现彩色,且亮度与背景不同,从而形成物体轮廓,边缘的明暗对比形成立体感,从而呈现浮雕效果。下图示意了处理过程

 

                            图4  原图                                                             图5 差分图

                                                                                          图7  结果图 

因为差分之后会存在负值,负值无法显示,因此需要一个基底衬托,一般取最大值的一半,即128,但不是绝对的。这就是图7中shift的作用,使暗边缘得以显示。

应用

图6到图7的过程可以看成是在一幅单色基底图像上叠加上差分图像,基底图像每个像素每个通道值均为shift,那么基底必须是单色图像吗,如果换成其他图像会呈现什么效果呢?下面我们尝试把一幅图像的差分图像叠加到另一幅图像上,使用图1作为基底,对图8作差分后叠加到基底上,看看会出现什么效果。

                                        

                                                                                                    图8 

代码如下

#include<iostream>  
#include <opencv2/core/core.hpp>  
#include <opencv2/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>  

using namespace std;

int main()
{
	int rows, cols, i, j, tmp;
	int alpha = 1;
	cv::Mat img1 = cv::imread("D:\\seal.jpg"); //已知seal.jpg宽高均小于timg.jpg
	cv::Mat img2 = cv::imread("D:\\timg.jpg"); //基底图像
	rows = img1.rows;
	cols = img1.cols;

	for (i = 0; i < rows ; i++)
	{
		cv::Vec3b* data1 = img1.ptr<cv::Vec3b>(i);
		cv::Vec3b* data2 = img2.ptr<cv::Vec3b>(i);
		for (j = 0; j < cols - 1; j++)
		{
			tmp = (-data1[j][0] + data1[j + 1][0])*alpha + data2[j][0];
			data2[j][0] = tmp < 0 ? 0 : tmp>255 ? 255 : tmp;
			tmp = (-data1[j][1] + data1[j + 1][1])*alpha + data2[j][1];
			data2[j][1] = tmp < 0 ? 0 : tmp>255 ? 255 : tmp;
			tmp = (-data1[j][2] + data1[j + 1][2])*alpha + data2[j][2];
			data2[j][2] = tmp < 0 ? 0 : tmp>255 ? 255 : tmp;
		}
	}
	cv::imshow("result", img2);
	cv::waitKey(100000);
	return 0;
}

 效果如下图所示,在图1左上角敲了个钢印。

                                                                                           图9  钢印效果 

 仔细观察可以发现,图9中的钢印中间部分不明显,这是由于只计算了x方向的差分,而印章中间部分在x方向变化小,因此差分值较小。如果叠加上y方向的差分图,就会比较明显。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值