利用滤波实现油画的浮雕和油画效果
一.浮雕效果
1.原理:
将图片的颜色或灰度变换较大的像素部分突出出来,而将灰度或颜色变换不大的部分淡化,使图像出现纵深感,从而达到浮雕效果。利用matlab的线性滤波函数filter2(),以及水平,垂直,45度,135度方向的算子进行滤波,最后叠加起来可以得到较好的浮雕效果。
2.matlab代码:(main1.m)
[filename, pathname] = uigetfile({'*.bmp;*.jpg;*.png;*.jpeg', 'Image Files (*.bmp, *.jpg, *.png,*.jpeg)'; ...
'*.*', 'All Files (*.*)'},'Pick an image');
if isequal(filename,0) || isequal(pathname,0),
return;
end %获取图片路径
path=fullfile([pathname,filename]);
RGB=imread(path);%读取一张图片
R=RGB(:,:,1);%红色分量表示的图像
G=RGB(:,:,2);%绿色分量表示的图像
B=RGB(:,:,3);%蓝色分量表示的图像
h1=[-1,-4,-1;0,0,0;1,4,1];%水平方向算子
IR1=filter2(h1,R);%对红色分量进行浮雕效果处理
IG1=filter2(h1,G);%对绿色分量进行浮雕效果处理
IB1=filter2(h1,B);%对蓝色分量进行浮雕效果处理
h2=[1,0,-1;4,0,-1;1,0,-1];%垂直方向算子
IR2=filter2(h2,R);
IG2=filter2(h2,G);
IB2=filter2(h2,B);
h3=[0,-1,-4;1,0,-1;1,4,0];%45度方向算子
IR3=filter2(h3,R);
IG3=filter2(h3,G);
IB3=filter2(h3,B);
h4=[4,1,0;1,0,-1;0,-1,-4];%135度方向算子
IR4=filter2(h4,R);
IG4=filter2(h4,G);
IB4=filter2(h4,B);
IR=IR1+IR2+IR3+IR4;
IG=IG1+IG2+IG3+IG4;
IB=IB1+IB2+IB3+IB4;
IMG=IR+IG+IB;%对rgb 三个分量单独处理后的图像进行叠加
subplot(1,2,1),imshow(RGB),title('原图');
subplot(1,2,2),imshow(IMG,[]),title('浮雕效果图');
3.实验结果:
4.问题与思考:
通过以上算法只能实现水平,垂直,45度和135度这四个方向上的浮雕效果,如何实现其它任意方向的浮雕效果?根据任意方向的导数算法,
我们可以使用多方向的梯度算法和拉普拉斯变换算法输出任意原
图浮雕效果图
方向最佳的边缘响应。其实,梯度算法和拉普拉斯算法输出的是
0~180度范围垂直方向对的最大响应,即:
B=max(|dn/dx |+|dn/dy|)
B=max(|d2n/dx2|+|d2n/dy2|)
二.油画效果
1.基本原理:检测图像每个像素的邻域,计算每个邻域像素的强度(亮度),取重复出现最多的值相对应的像素作为输出,分别取这些像素的r,g,b分量的平均值作为当前像素输出的r,g,b值,最终得到的图像会损失一些信息,得到类似油画的效果。
2.具体算法分析:
对于一副图像,例如:
(1)确定当前像素位置为(x,y),取邻域半径为2,则邻域范围
是从[x-2,y-2]到[x+2,y+2],如图:图中标出每个像素的r,g,b值。
(2)计算每个邻域像素的强度值,计算公式为:
强度值=[(r值+g值+b值)/3*强度范围]/255,其中强度范围是可调整的参数(该例子取值为20)。结果如图:
其中,强度值8出现次数最多,为7次,对应的像素位置分别为:[x-1,y-2],[x,y-1],[x,y+1],[x+1,y-1],[x+1,y],[x+1,y+2],[x+2,y+2]
(3)取这7个像素r分量的平均值作为输出像素r分量的值:R=(124+120+158+129+108+170+140)/7=135,
同理,可求出输出像素g分量和b分量的值,分别为128,65. (4)则当前像素(x,y)对应的输出值为:RGB(135,128,65)。
(5)改变当前像素位置,重复以上步骤等到每一个像素的输出值,最终输出的图像就具有油画效果。
3.算法参数分析:
邻域半径:确定邻域范围,取3到7可以得到较好的油画效果
强度范围:用来计算邻域像素的强度,从而达到筛选输出的目的
4.matlab代码:
oilpaint.m:
function [outputimage]=oilpaint(image,radius,intensity_level)
%image 输入的图片数组
%radius 滤波邻域的半径
%intensity_level 强度范围,由于计算像素强度
%outputimage 输出的图片数组
image=uint16(image);
%将输入的图片数组转为uint16型,避免计算时超出范围
image_size=size(image);
height=image_size(1); %图片的高度
width=image_size(2); %图片的宽度
outputimage=zeros(height,width,3);
%用一个同样大小的数组记录输出图像的数据,初始化为零
for x=1:height
for y=1:width
intensity_counter=zeros(intensity_level,1);
%强度计数器,记录邻域每个强度出现次数
sum_r=uint16(zeros(intensity_level,1));
%r分量累加器,记录每个强度r分量的和
sum_g=uint16(zeros(intensity_level,1)); %g分量累加器
sum_b=uint16(zeros(intensity_level,1)); %b分量累加器
for i=(x-radius):(x+radius)
for j=(y-radius):(y+radius) %邻域的遍历
if i>0&&i<=height&&j>0&&j<=width
intensity=(image(i,j,1)+image(i,j,2)+image(i,j,3))/3*intensity_level/255;
%计算每一像素的强度
if intensity==0
intensity=1;
end %强度不能为零,避免数组下标出现零
intensity_counter(intensity)=intensity_counter(intensity)+1;
%统计强度出现的次数
sum_r(intensity)=sum_r(intensity)+image(i,j,1);
%同一强度的r分量求和
sum_g(intensity)=sum_g(intensity)+image(i,j,2);
sum_b(intensity)=sum_b(intensity)+image(i,j,3);
end
end
end
intensity_counter_max=max(intensity_counter);
%找出同一强度出现最多的次数
for i=1:intensity_level
if intensity_counter(i)==intensity_counter_max
index=i;
end
end
%若出现最多的次数有重复,则取强度大的像素作为输出
outputimage(x,y,1)=sum_r(index)/intensity_counter(index);
%出现最多次的强度值对应像素的r分量取平均值,作为当前像素输出的r分量
outputimage(x,y,2)=sum_g(index)/intensity_counter(index);
%输出的g分量
outputimage(x,y,3)=sum_b(index)/intensity_counter(index);
%输出的b分量
end
end
outputimage=uint8(outputimage);%将uint16图像转化为matlab 默认的uint8型
main.m:
[filename, pathname] = uigetfile({'*.bmp;*.jpg;*.png;*.jpeg', 'Image Files (*.bmp, *.jpg, *.png,*.jpeg)'; ... '*.*', 'All Files (*.*)'},'Pick an image');
if isequal(filename,0) || isequal(pathname,0),
return;
end %获取图片路径
path=fullfile([pathname,filename]);
image=imread(path);%读取一张图片
outputimage=oilpaint(image,5,100);%通过滤波进行油画效果处理
subplot(1,2,1)
imshow(image);
title('原图');
subplot(1,2,2);
imshow(outputimage);
title('油画效果图');
5.实验结果:
原
图油画效果图
6.实现过程中的问题与思考:
(1)边界问题:这是一个非线性滤波,关键是强度重复像素的计算,不必进行边界的扩展。如果边界上补零,那么输出图片会有黑边;如果采用复制外边界来扩展,那么输出边界就是原图的边界;如果边界采用对称扩展或周期扩展,输出的油画效果和不采用边界扩展几乎没有是一样的。
(2)数据类型问题 :matlab 读取图片时默认为uint8类型的数组,但是在计算像素强度的过程中出现了远大于255的值,所以必须将uint8数组转换为uint16的数组进行计算分析,最后的结果再转换为uint8性的数组输出。
原
图油画效果图