💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、星火、月之暗面及文生图 广告
水彩画滤镜 水彩画滤镜算法如下: 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