水彩画滤镜
水彩画滤镜算法如下:
1,假设原始图像为F(x,y),灰度化得到G(x,y);
2,构建一个半径为Radius的正方形模板M,边长为2*Radius+1;
3,将M在F上依次遍历每个像素,对于当前像素P(x,y):
设置一个油漆桶数N,由于图像灰度值范围为0-255,因此我们油漆桶的数量N要小于255,这个油漆桶是用来盛放不同类别的像素。
3.1首先按照油漆桶数N将0-255的范围划分为等距的N个油漆桶,对于模板中对应的像素,我们按照其灰度值,依次将其放入相应的油漆桶中;
3.2统计N个油漆桶中的像素数目,计算像素数最多的那个油漆桶内,像素的均值Mean,这个均值RGB就是模板中心像素P(x,y)的值。
示意图如下:
[![](https://box.kancloud.cn/2016-01-05_568b3324d3a94.jpg)](http://www.zealpixel.com/data/attachment/portal/201507/25/125919y9s6p02da0y6m3p3.jpg)
Fig.1 油画滤镜示意图(N=8)
注意:油漆桶数N可以调节图像平滑度,模板半径Radius用来调节水彩画的水彩程度。
上述算法在进行模板遍历时,可以采用快速均值滤波算法的方法来提高效率。
代码如下:
~~~
private Bitmap OilpaintFilterProcess(Bitmap srcBitmap, int radius, int smooth)
{
if (radius == 0)
return srcBitmap;
smooth = smooth < 1 ? 1 : smooth;
smooth = Math.Max(1, smooth);
Bitmap a = new Bitmap(srcBitmap);
int w = srcBitmap.Width;
int h = srcBitmap.Height;
if (radius > Math.Min(w, h) / 2)
radius = (int)(Math.Min(w, h) / 2 - 0.5);
System.Drawing.Imaging.BitmapData srcData = a.LockBits(new Rectangle(0, 0, w, h), System.Drawing.Imaging.ImageLockMode.ReadWrite, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
IntPtr ptr = srcData.Scan0;
int bytes = h * srcData.Stride;
byte[] srcValues = new byte[bytes];
System.Runtime.InteropServices.Marshal.Copy(ptr, srcValues, 0, bytes);
byte[] tempValues = (byte[])srcValues.Clone();
int stride = srcData.Stride;
int i, j, k;
int unit = 4;
int[] gray_bt = new int[smooth];
int[] r_bt = new int[smooth];
int[] g_bt = new int[smooth];
int[] b_bt = new int[smooth];
int[] gray_bt_src = new int[smooth];
int[] r_bt_src = new int[smooth];
int[] g_bt_src = new int[smooth];
int[] b_bt_src = new int[smooth];
int r, g, b;
int gray = 0, bt_index = 0, max = 0, maxindex = 0;
i = 0;
bool frist = true;
int pos = 0;
for (j = 0; j < h; j++)
{
if (frist)
{
for (int m = -radius; m <= radius; m++)
{
for (int n = -radius; n <= radius; n++)
{
pos = Math.Abs(n) * unit + Math.Abs(m) * stride;
b = srcValues[pos++];
g = srcValues[pos++];
r = srcValues[pos];
gray = (b + g + r) / 3;
bt_index = gray * smooth >> 8;
gray_bt_src[bt_index]++;
b_bt_src[bt_index] += b;
g_bt_src[bt_index] += g;
r_bt_src[bt_index] += r;
}
}
Array.Copy(gray_bt_src, gray_bt, smooth);
Array.Copy(b_bt_src, b_bt, smooth);
Array.Copy(g_bt_src, g_bt, smooth);
Array.Copy(r_bt_src, r_bt, smooth);
max = 0;
maxindex = 0;
for (k = 0; k < smooth; k++)
{
if (max < gray_bt[k])
{
max = gray_bt[k];
maxindex = k;
}
}
pos = j * stride;
tempValues[pos++] = (byte)(b_bt[maxindex] / max);
tempValues[pos++] = (byte)(g_bt[maxindex] / max);
tempValues[pos] = (byte)(r_bt[maxindex] / max);
frist = false;
}
else
{
for (int m = -radius; m <= radius; m++)
{
pos = Math.Abs(m) * unit + Math.Abs(j - radius - 1) * stride;
b = srcValues[pos++];
g = srcValues[pos++];
r = srcValues[pos];
gray = (b + g + r) / 3;
bt_index = gray * smooth >> 8;
gray_bt_src[bt_index]--;
b_bt_src[bt_index] -= b;
g_bt_src[bt_index] -= g;
r_bt_src[bt_index] -= r;
pos = Math.Abs(m) * unit + Math.Abs(j + radius) % h * stride;
b = srcValues[pos++];
g = srcValues[pos++];
r = srcValues[pos];
gray = (b + g + r) / 3;
bt_index = gray * smooth >> 8;
gray_bt_src[bt_index]++;
b_bt_src[bt_index] += b;
g_bt_src[bt_index] += g;
r_bt_src[bt_index] += r;
}
Array.Copy(gray_bt_src, gray_bt, smooth);
Array.Copy(b_bt_src, b_bt, smooth);
Array.Copy(g_bt_src, g_bt, smooth);
Array.Copy(r_bt_src, r_bt, smooth);
}
for (i = 1; i < w; i++)
{
for (int m = -radius; m <= radius; m++)
{
pos = Math.Abs(i - radius - 1) * unit + Math.Abs(j + m) % h * stride;
b = srcValues[pos++];
g = srcValues[pos++];
r = srcValues[pos];
gray = (b + g + r) / 3;
bt_index = gray * smooth >> 8;
gray_bt[bt_index]--;
b_bt[bt_index] -= b;
g_bt[bt_index] -= g;
r_bt[bt_index] -= r;
pos = Math.Abs(i + radius) % w * unit + Math.Abs(j + m) % h * stride;
b = srcValues[pos++];
g = srcValues[pos++];
r = srcValues[pos];
gray = (b + g + r) / 3;
bt_index = gray * smooth >> 8;
gray_bt[bt_index]++;
b_bt[bt_index] += b;
g_bt[bt_index] += g;
r_bt[bt_index] += r;
}
max = 0;
maxindex = 0;
for (k = 0; k < smooth; k++)
{
if (max < gray_bt[k])
{
max = gray_bt[k];
maxindex = k;
}
}
pos = i * unit + j * stride;
tempValues[pos++] = (byte)(b_bt[maxindex] / max);
tempValues[pos++] = (byte)(g_bt[maxindex] / max);
tempValues[pos] = (byte)(r_bt[maxindex] / max);
}
}
srcValues = (byte[])tempValues.Clone();
System.Runtime.InteropServices.Marshal.Copy(srcValues, 0, ptr, bytes);
a.UnlockBits(srcData);
return a;
}
~~~
效果图如下:
[![](https://box.kancloud.cn/2016-01-05_568b33241586c.jpg)](http://www.zealpixel.com/data/attachment/portal/201507/25/130152shnhd3ranthdr4wz.jpg)
原图
[![](https://box.kancloud.cn/2016-01-05_568b3324eba0d.png)](http://www.zealpixel.com/data/attachment/portal/201507/25/130151xeuqbue4ggj68e4c.png)
水彩画滤镜效果图
最后,放上一个完整的C#版程序DEMO下载地址:http://www.zealpixel.com/thread-61-1-1.html
- 前言
- 序言
- Brannan滤镜
- 编码基础(Photoshop基础变换的代码实现)
- Toaster滤镜
- Hudson滤镜(Instagram)
- 暴雨滤镜
- 大雪滤镜
- 图像滤镜实现万能方法研究
- 大雾效果滤镜
- 连环画滤镜
- 暗调滤镜
- 怀旧风格滤镜
- (Nostalgla Filter)老照片滤镜
- (Punch Filter)交叉冲印滤镜
- (Lightleaks Filter)漏光滤镜
- 漫画滤镜
- LOMO Filter
- Glow Filter发光滤镜
- (Instagram)1977滤镜
- (Sketch Filter)素描滤镜
- 水彩画滤镜
- 图像光照效果滤镜
- Oilpaint油画滤镜
- Swirl滤镜
- Wave滤镜
- 球面(Spherize)滤镜
- 挤压(Pinch)滤镜
- 旋转模糊滤镜
- 霓虹、浮雕、木刻滤镜
- 图像滤镜晕影调节算法研究
- PS平均(滤镜-模糊-平均)效果
- Photoshop实现Instagram Amaro滤镜特效
- Photoshop实现Instagram之Nashville滤镜
- Photoshop实现Instagram之Sierra滤镜
- Photoshop实现Instagram之Mayfair滤镜效果
- ZPhotoEngine超级算法库
- 乐高像素拼图特效
- 乐高像素拼图特效滤镜的代码实现
- 保留细节的磨皮滤镜之PS实现
- 保留细节的磨皮之C#程序实现
- 流行艺术风滤镜特效PS实现
- PS图层混合模式之明度模式