PhotoShop算法原理解析系列 - 像素化---》碎片。
接着上一篇文章的热度,继续讲讲一些稍微简单的算法吧。
本文来讲讲碎片算法,先贴几个效果图吧:
这是个破坏性的滤镜,拿美女来说事是因为搞图像的人90%是男人,色色的男人。
关于碎片滤镜的原理,网络上可找到的资料为:将图像创建四个相互偏移的副本,产生类似重影的效果。
就凭上述一句话,我们就可以动手了。
分析:通过上述几幅图像的比较,特别是眼睛部位,可以看出处理的图应该看得出像是单眼变成了4个眼睛,因此,网络上的说法可靠。
那么偏移的中心在哪里,偏移的数量又是多少呢,4个偏移,分别是往那些方向偏移呢,这些问题也很简单,可以那PS做验证:
具体步骤如下:打开一幅图像,在图像颜色比较单调的地方(比如上述美女的手臂处)填充一处2*2像素的红色,然后复制图层,对复制后的图层进行碎片滤镜处理,并调整图层透明度为50%,局部放大可得到如下图像:
如此效果,则可轻易得出结论:
偏移的中心就是以每个像素为中心,4个偏移分别以中心对称,斜45度均匀圆周布置,水平和垂直偏移各45度,偏移量4个像素。
那么如何叠加的问题应该可以猜测,是取四次偏移后累加值的平均值。
针对如此思路,我写出如下算法:
private void CmdFragment_Click(object sender, EventArgs e)
{
int X, Y, Z, XX, YY;
int Width, Height, Stride;
int Speed, Index;
int SumR, SumG, SumB;
Bitmap Bmp = (Bitmap)Pic.Image;
if (Bmp.PixelFormat != PixelFormat.Format24bppRgb) throw new Exception("不支持的图像格式."); Width = Bmp.Width; Height = Bmp.Height; Stride = (int)((Bmp.Width * + ) & 0XFFFFFFFC); byte[] ImageData = new byte[Stride * Height]; // 用于保存图像数据,(处理前后的都为他)
byte[] ImageDataC = new byte[Stride * Height]; // 用于保存克隆的图像数据
int[] OffsetX = new int[] { , -, -, }; // 每个点的偏移量
int[] OffsetY = new int[] { -, -, , };
fixed (byte* P = &ImageData[], CP = &ImageDataC[])
{
byte* DataP = P, DataCP = CP;
BitmapData BmpData = new BitmapData();
BmpData.Scan0 = (IntPtr)DataP; // 设置为字节数组的的第一个元素在内存中的地址
BmpData.Stride = Stride;
Bmp.LockBits(new Rectangle(, , Bmp.Width, Bmp.Height), ImageLockMode.ReadWrite | ImageLockMode.UserInputBuffer, PixelFormat.Format24bppRgb, BmpData); Stopwatch Sw = new Stopwatch(); // 只获取计算用时
Sw.Start();
System.Buffer.BlockCopy(ImageData, , ImageDataC, , Stride * Height); // 填充克隆数据 for (Y = ; Y < Height; Y++)
{
Speed = Y * Stride;
for (X = ; X < Width; X++)
{
SumB = ; SumG = ; SumR = ;
for (Z = ; Z < ; Z++) // 累积取样点的取样和
{
XX = X + OffsetX[Z];
YY = Y + OffsetY[Z];
if (XX < ) // 注意越界
XX = ;
else if (XX >= Width)
XX = Width - ;
if (YY < )
YY = ;
else if (YY >= Height)
YY = Height - ;
Index = YY * Stride + XX * ;
SumB += DataCP[Index];
SumG += DataCP[Index + ];
SumR += DataCP[Index + ];
} DataP[Speed] = (byte)((SumB+) >> ); // 求平均值(Sum+2)/4,为什么要+2,就为了四舍五入。比如如果计算结果为108.6,则取像素109更为合理
DataP[Speed + ] = (byte)((SumG + ) >> );
DataP[Speed + ] = (byte)((SumR + ) >> );
Speed += ; // 跳往下一个像素
}
}
Sw.Stop();
this.Text = "计算用时: " + Sw.ElapsedMilliseconds.ToString() + " ms";
Bmp.UnlockBits(BmpData); // 必须先解锁,否则Invalidate失败
}
Pic.Invalidate();
}
算法中,OffsetX 和 OffsetY分别为取样点像素的偏移量。同样,由于该滤镜涉及到了领域操作,在处理前需要做像素备份,但这里没有对备份数据进行扩展。因此,在内部代码里就需要对取样点的坐标进行验证,看是否超过其范围,如果超过范围,通常在图像滤镜算法范围内,有3种处理方式:
(1)超过了则认为是其最接近的边界值,即重复边缘像素,这部分代码即上述贴出的if ..... else if 部分。
(2)折回,可用如下代码来描述:
while (XX >= Width)
XX = XX - Width;
while (XX < )
XX = XX + Width;
while (YY >= Height)
YY = YY - Height;
while (YY < )
YY = YY + Height;
(3) 只计算在图像范围内的像素:
if (XX >= && XX < Width && YY >= && YY < Height)
{
// 累加计算
}
当然这样做,就必须用一个变量记录下都做了多少次符合条件的计算。
有兴趣的朋友可以自己改改代码试一试。
上述代码段中DataP[Speed] = (byte)((SumB+2) >> 2);要对SumB加2的原因是为了让结果进行四舍五入的操作,这样才较为合理。
经过测试,上述代码和PS处理的效果100%的吻合。说明我们的猜测是完全正确的。
还可以对算法进一步扩展: 想的远一点,为什么非的是4个重影呢,非得是45度角度呢,非得是4个像素的水平和垂直偏移呢。我给出下图让有兴趣的读者自己研发吧。
图中,角度为32度,半径为10,碎片数为7,可产生类似下面的效果(可用我的Imageshop进行验证):
完整工程下载地址:http://files.cnblogs.com/Imageshop/Fragement.rar
***************************作者: laviewpbt 时间: 2013.7.5 联系QQ: 33184777 转载请保留本行信息*************************
PhotoShop算法原理解析系列 - 像素化---》碎片。的更多相关文章
- PhotoShop算法原理解析系列 - 风格化---》查找边缘。
之所以不写系列文章一.系列文章二这样的标题,是因为我不知道我能坚持多久.我知道我对事情的表达能力和语言的丰富性方面的天赋不高.而一段代码需要我去用心的把他从基本原理-->初步实现-->优化 ...
- 2. Attention Is All You Need(Transformer)算法原理解析
1. 语言模型 2. Attention Is All You Need(Transformer)算法原理解析 3. ELMo算法原理解析 4. OpenAI GPT算法原理解析 5. BERT算法原 ...
- 3. ELMo算法原理解析
1. 语言模型 2. Attention Is All You Need(Transformer)算法原理解析 3. ELMo算法原理解析 4. OpenAI GPT算法原理解析 5. BERT算法原 ...
- 4. OpenAI GPT算法原理解析
1. 语言模型 2. Attention Is All You Need(Transformer)算法原理解析 3. ELMo算法原理解析 4. OpenAI GPT算法原理解析 5. BERT算法原 ...
- 5. BERT算法原理解析
1. 语言模型 2. Attention Is All You Need(Transformer)算法原理解析 3. ELMo算法原理解析 4. OpenAI GPT算法原理解析 5. BERT算法原 ...
- FastText算法原理解析
1. 前言 自然语言处理(NLP)是机器学习,人工智能中的一个重要领域.文本表达是 NLP中的基础技术,文本分类则是 NLP 的重要应用.fasttext是facebook开源的一个词向量与文本分类工 ...
- LRU算法原理解析
LRU是Least Recently Used的缩写,即最近最少使用,常用于页面置换算法,是为虚拟页式存储管理服务的. 现代操作系统提供了一种对主存的抽象概念虚拟内存,来对主存进行更好地管理.他将主存 ...
- 最全排序算法原理解析、java代码实现以及总结归纳
算法分类 十种常见排序算法可以分为两大类: 非线性时间比较类排序:通过比较来决定元素间的相对次序,由于其时间复杂度不能突破O(nlogn),因此称为非线性时间比较类排序. 线性时间非比较类排序:不通过 ...
- CGI原理解析系列之中的一个----CGI怎样获取WEBserver数据
//gcc get_post.c -o get_post.ums; #include <stdio.h> #include <stdlib.h> #include <un ...
随机推荐
- 模仿36。杀毒~button
<Style x:Key="360btn" TargetType="{x:Type Button}"> <Setter Property=&q ...
- XML 动态 插入 父类标示 子类标示
由于第一次写博客,写的不好的地方,还请各位大神多多指点, 讲解一下:xml动态插入数据并保存,写这个时候费了我不少劲,最后终于皇天不负有心人让我搞出来了,特意分享给大家,写的不完美的地方还请大家多多指 ...
- 从零开始学Python第七周:面向对象进阶(需修改)
一,类的属性 (1)示例 通过属性获取已经创建对象的个数 class Plane: pCount = 0 #类属性 def __init__(self,name,category): self.nam ...
- Lind.DDD.UoW~方法回调完成原子化操作
回到目录 本文来自于实践中的不足 在最近开始过程中,遇到了一个问题,之前设计的工作单元UoW只支持Insert,Update,Delete三种操作,即开发人员可以将以上三种操作同时扔进工作单元,由工作 ...
- 十一个行为模式之解释器模式(Interpreter Pattern)
定义: 定义一个语言的文法,可以使用一个解释器来解释其文法.定义终结符和非终结符的统一接口,并使用抽象对象建立非终结符与其它元素的关联. 结构图: AbstractExpression:抽象表达式类, ...
- 线程.FTP.SFTP.打包
Windows就是多线程模式.每一个解决方案就是一个进程.一个进程下拥有多个线程. 简单点.单核的处理器不存在多线程.是CPU在每一个线程上切换处理.在人反应不过来的情况下完成同步的效果. 比如左手画 ...
- 设计模式学习之路——Facade 外观模式(结构型模式)
动机: 组件的客户和组件中各种复杂的子系统有了过多的耦合,随着外部客户程序和各子系统的演化,这种过多的耦合面临很多变化的挑战.如何简化外部客户程序和系统间的交互接口?如何将外部客户程序的演化和内部子系 ...
- Inplace Search on document libraries and lists is not working
[http://sharepointfarmer.com/inplace-search-on-document-libraries-and-lists-is-not-working/] I ran i ...
- eclipse启动时报错 Could not create the java virtual machine
eclipse启动的时候 报错 这是系统为eclipse分配的内存不足,需要去修改 eclipse.ini文件 eclipse.ini是eclipse 内存分配之类的配置文件 对java虚拟机的 ...
- Jquery——简单的视差滚动效果,兼容PC移动端
$(function(){ $(window).scroll(function(){ var top=$(this).scrollTop(); $(". ...