gif反转工具

首先看下效果图: 然后是两张gif的对比 //原本图是正着走的 //处理后是倒着走的 gif是动态的嘛。然后我昨天和一个朋友聊天的时候发了一串相同的gif图,然后看着千篇一律的东西。我想能不能写个程序实现gif的初始状态不同呢。什么意思呢。我们知道,gif是由帧构成的,我想实现的功能是比如一个gif共有十帧,那么我写出来的程序能够生成10个gif文件,分别对应不同的初始状态来进行循环。后来一想,gif帧太多的话,比较慢,而且也不实用,于是决定简化一下,只做一个反转工具,比如一个gif是从左到右播放的,通过这个成功可以生成一个相同的gif图,不过是倒着播放的。 思路很简单,就是先把gif分解成很多帧,然后对帧进行合并,合并帧之前把帧的位置反转一下就可以了。因为我自己对图像处理的知识不懂,只想到了思路,所以这些功能都要找些资料,然后修改,测试。 分割帧的代码如下 //解码gif图片 public List<string> GetFrames(string pPath, string pSavedPath) { Image gif = Image.FromFile(pPath); FrameDimension fd = new FrameDimension(gif.FrameDimensionsList[0]); //获取帧数(gif图片可能包含多帧,其它格式图片一般仅一帧) int count = gif.GetFrameCount(fd); List<string> gifList=new List<string>(); //以Jpeg格式保存各帧 for (int i = 0; i < count; i++) { gif.SelectActiveFrame(fd, i); gif.Save(pSavedPath + "\\frame_" + i + ".png", ImageFormat.Png); gifList.Add(pSavedPath + "\\frame_" + i + ".png"); } return gifList; } 可以看到,返回了一个包含所有生成的帧地址的list列表。然后就是使用gifList作为参数来合并了。 //获取系统临时目录存放解码后的png图片 string temppath = System.Environment.GetEnvironmentVariable("TEMP"); List<string> gifList = GetFrames(tBoxFile.Text, temppath); gifList.Reverse(); String outputFilePath = "new.gif"; AnimatedGifEncoder ae = new AnimatedGifEncoder(); ae.Start(outputFilePath); ae.SetDelay(100); // 延迟间隔 ae.SetRepeat(0); //-1:不循环,0:总是循环 播放 for (int i = 0, count = gifList.Count; i < count; i++) { ae.AddFrame(Image.FromFile(gifList[i])); } ae.Finish(); MessageBox.Show("成功!新文件已保存在同目录"); 这里面使用了AnimatedGifEncoder这个类,这是Gif.Components.dll动态连接库里的类(此库开源,文末给出地址),是我在codeProject上找到的。首先我把gifList反转,然后合并保存到同目录。中间生成的帧为了方便我保存到了temp目录。 本来这个库里是分割gif的功能的。但是我实际测试后发现效果非常差,图片黑条泛滥,根本没法看。所以还是使用上面那段代码,相关代码我依然保存在工程里,有兴趣可以自己测试。 明天四级考试,求人品。。 项目源码:gif反转工具 参考: C#图片处理:获取GIF 动画图片中的各个帧 NGif, Animated GIF Encoder for .NET

2012-06-15 · 1 min · bystander

VS2010 编译安装boost库

实践是最好的办法。。学习C++,想试试线程,然后打算用boost库,结果boost库编译差点吓到我。。没看到比较完整的安装教程。。一直耽搁。今天动手。完成了。方法记录如下: 1.下载boost 从boost官网( http://www.boost.org )上下载最新的boost版本,现在最新是1.49版本,解压到自定义目录(我解压到了D:/program files,最终的目录结构是D:\Program Files\boost_1_49_0) 2.编译安装 在D:\Program Files\boost_1_49_0的目录下,有一个bootstrap.bat文件,直接双击运行。就会在同目录生成b2.exe;bjam.exe两个文件。 3.设定编译环境 修改user-config.jam (D:\Program Files\boost_1_49_0\tools\build\v2\user-config.jam) 的MSVC configuration MSVC configuration Configure msvc (default version, searched for in standard locations and PATH). using msvc ; 在上面这段的下面直接添加如下的文字。 using msvc : 10.0 : :/wd4819/D_CRT_SECURE_NO_DEPRECATE/D_SCL_SECURE_NO_DEPRECATE/D_SECURE_SCL=0 ; 保存关闭。 4.开始编译 点击开始->所有程序->“Microsoft Visual Studio 2010”,指向“Visual Studio tools(工具)”,然后单击“Visual Studio 2010 command prompt(命令提示)” 使用cd切换到D:\Program Files\boost_1_49_0目录。这个就不说了 然后输入如下的代码: b2 toolset=msvc-10.0 architecture=x86 instruction-set=i686 address-model=32 link=static variant=debug,release threading=multi runtime-link=shared --without-python --without-mpi --without-wave --without-graph --without-math --without-serialization stage 解释一下命令的意思: 1.toolset:表示编译器工具,我安装的是VS2010,所以是msvc-10(如果你是VS2005,可以使用msvc-8.0 VS2008是msvc-9.0) 2.architecture:表示架构,也就是你的CPU架构,x86,x64,因为我安装的是win7 32位,所以使用了x86的架构 3.instruction-set:表示指令集,依然是8086指令集 4.address-model:表示地址长度为32位 5.link:表示生成动态/静态链接库,动态链接库是shared,静态链接库是static,一般都会编译成静态库,因为给出程序的时候打包boost的库会非常庞大 6.variant:表示生成的Debug或者release版本,一般情况下会两种版本都会编译出来的 7.threading:表示单/多线程编译,一般我们的程序都会用到多线程,所以选择了multi 8.runtime-link:表示动态/静态链接C/C++运行时库(C/C++ Runtime),我们选择了动态链接 9.without/with:表示不需要编译/需要编译哪些库,一些自己不用的库可以无需编译 10.stage/install:stage表示只生成库文件(DLL和Lib),install还会生成包含头文件的include目录,推荐使用stage,因为boost_1_49\boost中就是boost库完整的头文件,所以无需再拷贝一份出来。编译出来的库会放在stage文件夹中 这样一份完整的boost库就生成了,剩下就是直接使用到项目中了。 其实编译的具体命令都是可以自己写的。如果你需要编译所有。只需要使用下面的这行代码 b2 –toolset=msvc-10.0 –build-type=complete 就可以了。 不出问题的话。就开始编译了。。登个半个多小时吧。就会完成了。 5.设置vs 打开vs,新建一个工程。然后工程属性。配置属性->C/C++ ,附加包含目录 填上 D:\Program Files\boost_1_49_0;%(AdditionalIncludeDirectories) 这个是最终的结果,你也可以手动添加 在左侧选择链接器->附加库目录,填上 D:\Program Files\boost_1_49_0\stage\lib;%(AdditionalLibraryDirectories) 就可以了。 6.测试 在你新建的工程里输入如下的代码。运行成功就说明可以了 #include <boost/thread/thread.hpp> #include <iostream> void hello() { std::cout << "Hello world, I'm a thread!" << std::endl; } int main(int argc, char* argv[]) { boost::thread thrd(&hello); thrd.join(); return 0; } 参考: http://www.cppfans.org/1317.html http://www.cnblogs.com/ComputerG/archive/2011/03/10/1979730.html http://www.cppblog.com/shaker/archive/2011/11/30/33583.html

2012-05-24 · 1 min · bystander

c & c++中sizeof返回值不同?

问题: A character array is defined globally and a structure with same name is defined within a function. 一个字符数组被定义为全局变量,一个相同名字的结构体被定义在一个函数内部。 Why sizeof operator returns different values for c & c++ ? 为什么sizeof操作符对于C和C++返回了不同的值呢? char S[13]; void fun() { struct S { int v; }; int v1 = sizeof(S); } // returns 4 in C++ and 13 in C 答案: Because in C++, the struct you defined is named S, while in C, 因为在C++中,你定义的结构体的名称是S,而在C中, it’s named struct S (which is why you often see typedef struct used in C code). 他叫做struct S(这也是为什么我们可以经常看到typedef struct 被用在C代码中)。 In C, to refer to the struct type, you need to say struct S. Therefore, sizeof(S) refers to the array. 在C中,引用一个结构类型的时候,你必须说struct S,因此,sizeof(S)调用的是数组S。 In C++, struct is unnecessary. So the local S hides the global S. 在C++中,struct这个字不是必需的,所以局部变量S隐藏了全局变量S。 If you were to change the code to the following, you would get the expected results: 如果你把代码改成下面的样子,你就能得到你期望的结果了。 ...

2012-05-22 · 1 min · bystander

为什么sizeof(str.substr(0,3).c_str())=8?

问题: string str = "abcdefgdcb"; cout &lt; &lt; sizeof(str.substr(0,3).c_str()); For some reason, the above string is giving me 8. I assumed c_str() returns a null string, 由于某些原因,上面的这个字符串得到的结果是8,我估计c_str()返回了一个null, and sizeof uses the null to determine the size of the string. 并且sizeof函数使用这个null来定义这个字符串的大小。 答案: Because sizeof doesn’t give you the length of a string, 因为sizeof给你的不是一个字符串的长度, it gives you the size of the type (const char * in this case). Try strlen. 他给你的是这个类型的大小(这种情况下的类型是c_str()返回的const char*类型),想要得到正确的结果,试试strlen函数吧。 On your system, sizeof (const char ) == 8, like any other pointer. 在你的系统上,sizeof(const char)=8,和其他所有的指针类型一样。 8 is the size of a pointer on your machine (64-bit) 8是在你的64位电脑上一个指针的大小 There’s your problem. sizeof tells you the size of a variable, 别乱假设,sizeof告诉你一个变量的大小, which has nothing to do with the value inside the variable, ever. 他不会进入变量里面对变量做任何改变的。。永远不会。 问题:http://stackoverflow.com/q/10668764/764869

2012-05-21 · 1 min · bystander

对象数组的Xml序列化和反序列化

使用Serialization来进行序列化和反序列化,因此需要引入System.Xml.Serialization;命名空间。 为什么要做序列化和反序列化? .Net程序执行时,对象都驻留在内存中;内存中的对象如果需要传递给其他系统使用;或者在关机时需要保存下来以便下次再次启动程序使用就需要序列化和反序列化。 本文的原始例子在参考文中,但是参考文中没有给出反序列化的例子,且xml文件不清晰。于是修改了代码,同时实现对象数组序列化和反序列化。 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Xml.Serialization; using System.IO; namespace UseXmlSerialization { class Program { static void Main(string[] args) { //声明一个猫对象 var cWhite = new Cat { Color = "White", Speed = 10, Saying = "I am a good WHITE cat" }; var cBlack = new Cat { Color = "Black", Speed = 10, Saying = "I am a good BLACK cat" }; CatCollection cc = new CatCollection { Cats = new Cat[] { cWhite, cBlack } }; //序列化这个对象 XmlSerializer serializer = new XmlSerializer(typeof(CatCollection)); StringWriter sw = new StringWriter(); serializer.Serialize(sw, cc); //序列化到StringWriter对象sw里 string strXml = sw.ToString(); Console.WriteLine("对象数组序列化后:"); Console.WriteLine(strXml); Console.WriteLine("xml反序列化后:"); StringReader sr = new StringReader(strXml); CatCollection c2 = serializer.Deserialize(sr) as CatCollection; for (int i = 0; i < 2; i++) //验证xml文件是否已经被反序列化为对象数组 { Console.WriteLine("I am Cat " + i); Console.WriteLine(c2.Cats[i].Color); Console.WriteLine(c2.Cats[i].Saying); Console.WriteLine(); } } } //以下为类定义 [XmlRoot("Cats")] public class CatCollection { [XmlElement("Cat")] public Cat[] Cats { get; set; } } public class Cat { //定义Color属性的序列化为cat节点的属性 [XmlAttribute("color")] public string Color { get; set; } //要求不序列化Speed属性 [XmlIgnore] public int Speed { get; set; } //设置Saying属性序列化为Xml子元素 [XmlElement("saying")] public string Saying { get; set; } } } 参考: http://www.cnblogs.com/yukaizhao/archive/2011/07/22/xml-serialization.html

2012-05-18 · 1 min · bystander

《乌合之众》笔记上部

 今天本来是打算写篇关于LR型语法分析的总结的。结果因为各种原因错过了。可能我还是有轻微的拖延症吧。但是还是非常给力的。今天在读这本书-乌合之众-大众心理研究,本来就想着也就不到200页的书,几个小时就看完了,结果真正看了以后,一阵惊叹啊。信息量很大,现在才看到84页。不过笔记已经写了2000字了。为了避免文章太长。先分享这两千字。明天看完了后半部分,再分享。 群体的无意识行为代替了个人的有意识行为,是目前这个时代的主要特征之一。 社会组织就像一切生命有机体一样复杂,我们还不具备强迫他们在突然之间发生深刻变革的智力。 对于一个民族有致命危险的,莫过于他热衷于重大的变革。无论这些变革从理论上来说多么的出色。如果它能够使民族气质即刻产生变化,才能说他是有用的,只有时间才具备这样的力量, 如果有关某种形态的知识只有少数有学问的人才能掌握,那他也没有多少意义了。 造成文明洗心革面的唯一重要的变化,是影响到思想,观念和信仰的变化,令人难忘的历史事件,不过是人类思想不露痕迹的变化所造成的可见后果而已。 群体的力量会成为唯一无可匹敌的力量,我们要进入的时代,就是一个群体的时代。 群体不善推理,却急于采取行动,他们目前的组织赋予他们巨大的力量。 科学为我们许诺的是真理,从未许诺过和平或幸福,他对我们的感情无动于衷,对我们的哀怨不闻不问。 历史告诉我们,当文明赖以建立的道德因素失去威力时,他的最终解体总是有无意识的野蛮群体完成的, 创造和领导着文明的,历来就是少数知识贵族而不是群体,群体只具有强大的破坏力。 不管情况如何,我们注定要屈从于群体的势力,这是因为群体的目光短浅,使得有可能让他守规矩的所有障碍已经被一一清除。 一个心理群体表现出来的最惊人的特点是:不论他们是谁,他们变成了一个群体这个事实,使他们获得了一种集体信息,这是他们的感情,思想和行为变得与他们独自一人时颇为不同, 群体一般只有很普通的品质,他削弱了个人的优秀品质,这一事实解释了为何他不能完成需要高智力的工作,群体中累加在一起的只有愚蠢而不是天生的智慧。 从数量上来看,群体的个人会感觉到一种势不可挡的力量,使他敢于发泄出自己本能的欲望。而独自一人时,则必须克制欲望,因为他觉得:群体是个无名氏,不许承担责任。 还有就是群体的传染性,每种感情和行动都有传染性,其程度足以使个人随时准备为群体利益牺牲他的个人利益。这与人的天性相对立,如果不是群体的一员,很少具备这样的能力 长时间融入群体行动的个人,不就会发现,或许是因为在群体发挥催眠影响的作用下,自己进入一种特殊状态,类似于被催眠,变成了受人支配的无意识的努力,有意识的人格消失的无影无踪,意识和辨别力也不复存在。 1989年8月4日,的晚上,法国的贵族一时激情澎湃,毅然投票放弃了自己跌特权。所以说,群体有时候也是英雄群体。 群体容易被暗示,会随时听命于一切暗示。失去了批判能力。 不要相信童言无忌,儿童一直就在撒谎。 群体情绪的简单和夸张所造成的结果是,他全然不知怀疑和不确定性为何物,他就像女人一样,一下子就会陷入极端,怀疑一说出口,立刻就会变成不容辩驳的证据。 群体只知道简单而极端的感情;提供给他们的各种意见,想法和信念,他们或者全盘接受,或者一概拒绝,将其视为绝对真理或绝对谬论。 群体中只要有人在他们之间山东,他们随时都会付诸行动,群体对强权俯首帖耳,却很少为仁慈心肠所动,他们认为那不过是软弱可欺的另一种形式。他们的同情心从而不听命于作风温和的主子,而是只向眼里欺压他们的暴君低头。他们总是为这种人塑其最壮观的雕像。 群体强烈的守着无意识因素的支配,因此很容易屈从于世俗的等级制,难免会十分保守,对他们撒手不管,很快他们就会对混乱感到厌倦,本能的变成奴才。 观念只有采取简单明了的形式,才能被群体所接受,因此它必须经过一番彻底的改造,才能变得通俗易懂。 就观念来说,群体总是落后于博学之士和哲学家好几代人。 群体形象化的想象力不但强大而活跃,并且非常敏感。历史上,表相总比真相起着更重要的作用,不现实的因素总比现实的因素更加重要。 不管刺激群体想象力的是什么,采取的形式都应该具有鲜明形象。没有任何多余的解释。比如死亡人数。 如果一个民族使自己的习俗变得过于牢固,他便不会再发生变化,于是就像中国一样,变得没有改进能力。在这种情况下,暴力革命也没有多少用处,因此由此造成的结果,或者是打碎的锁链被重新拼接在一起,让整个过去原封不动的再现,或者是对被打碎的事务撒手不管,衰败很快被无政府状态所取代。因此对于一个民族来说,理想的状态是保留过去的制度,用一种不易察觉的方式一点点的加以改进。 群体具有保守主义精神,即使被狂暴的反叛最终也只会造成一些嘴皮子上的变化。 各民族是受着自己的性格支配的,凡是与这种性格不合的模式,都不过是借来的外套,一种暂时的伪装。 教育既不会使人变得更加道德,也不会使人更幸福,他既不能改变他的本能,也不能改变他的热情。。。害处远大于好处。 掌握一些派不上用场的知识,是让人造反的不二法门 生活中取得成功的条件是判断力,经验,开拓精神,和个性。非课本能带来。 大学毕业,一个充分发展的人诞生,但筋疲力尽,成家立业,落入生活的俗套,只要落入这种俗套,他就会把自己封闭在狭隘的职业中,工作还算本分,但仅此而已,这就是平庸的生活。 因为各种词语和套话的力量而死去的人,只要用他们的尸骨,就能建造一座比古老的齐奥普斯更高的金字塔。

2012-05-10 · 1 min · bystander

C++回调函数

 回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用为调用它所指向的函数时,我们就说这是回调函数。 也就是说,回调函数它首先是一个函数,然后有一个指针指向它(该指针称为函数指针),在别的代码块中,通过这个函数指针调用了这个函数,仅此而已。 下面给出一个例子,我写出了比较详细的注释。希望足够清晰。这个例子说明了,回调函数可以把调用者和被调用者分开,对于调用者来说,只需要知道自己要调用一个函数,该函数有一个string类型的参数,至于具体调用哪个,被调用的函数到底怎么执行,怎么解释该参数,是完全不用关心的。 #include #include using namespace std; typedef void (*PF)(string s); //定义一个名为PF的函数指针,该指针指向一类函数,该类函数有一个string类型的参数,返回值为void。 void funcOne(string s) //回调函数1 { cout << s+" One"<< endl; } void funcTwo(string s) //回调函数2 { cout << s+" Two"<< endl; } void caller( PF pf, string s) //调用函数 { cout << "I am Caller Function" << endl; pf(s); } int main() { string str = "Test CallBack Function"; PF pf1 = funcOne; //实例化一个函数指针,指向func函数 caller(pf1, str); pf1=funcTwo; caller(pf1, str); system("pause"); return 0; } 回调函数的一个很实用的例子就是泛型算法中的max算法,具体可以参见本文末尾的第一篇参考文档, 如果你在自行写代码中发生了 error C2679: 二进制“«”: 没有找到接受“std::string”类型的右操作数的运算符(或没有可接受的转换)错误 说明你没有把string头文件导入,导入即可 个人能力有限,错误之处请留言指正,不胜感激。 参考: http://learn.akae.cn/media/ch24s05.html http://baike.baidu.com/view/414773.htm

2012-05-09 · 1 min · bystander

你会用计算器吗?

 今天早上在用windows自带的计算器转换进制的时候,看到了下图所示的按钮。MS MR之类的。 这些个按钮在简单的计算器上也有。我从小时候到现在都没搞清楚。当然也没搞过。。不学总是不会的。于是,找找资料。学会了也分享一下,英文是我猜的。。不过估计差不多 首先明确的一点是这类计算器内部有一个小的记忆芯片,可以用来存储一个数,类似于内存吧。所以M的意思就是Memory,下面先给出这几个的总体说明 “MS”,英文 Memory Store,用来存储输入栏显示的数字。 “MR”,英文 Memory Read, 再次显示调用存储的数字。 “M+”,英文 Memory Plus, 存储器里的值加上输入栏的值,结果又存入存储器 “M+”,英文 Memory Minus, 存储器里的值减去输入栏的值,结果又存入存储器 “MC”,英文 Memory Clear,用于清除存储器中的数值,默认为0 “C”, 英文 Cancel,就是全部撤销; “CE”, 英文 Cancel Error,也就是撤销错误输入。 现在来说个例子,比如我要计算1002+113因为一些计算器不支持整个式子输入。也是为了演示这些功能。我们可以这样输入, 先输入100,然后 * ,然后 2 ,按下等号,这时候输入栏变成了200,我们按下MS 或者M+,按下MS的话把200存到了存储器,而按下M+呢,因为存储器默认是0,所以就相当于0+200,存储器里就是200了。然后我们继续输入11 ,输入 +,输入3 ,按下等号,输入栏变成了33.我们按下M+,这时候输入栏并没有改变。因为M+将存储器里的200加上了33.则存储器里变成了233.我们按下MR就是读取存储器的值,这样输入栏就可以看到233了。我们就可以继续用233来运算了。MC就是清除233.恢复为0. 例如:想要9*6,如果按6按错按成5了, 按C就是从头来过, 这时就要重新按9了, 但是如果你按CE的话, 就只要输入6就行了, 不必输入前面的了。 我个人感觉M存储器就相当于一个草稿。吧计算中的一些临时值存储起来,就不用手记了。我记得我那时候有时候算值还得先把一些临时值写在纸上,后面重新输入。没文化真可怕。

2012-05-05 · 1 min · bystander

WWWScan GUI版--WebScan

 网上一直有个版本是Wscan Gui Beta6,首先感谢作者的无私奉献,写代码不容易啊。但是这个我不知道为什么在我的电脑上总是有一些错误。很多功能虽然加上了,但是其实对我有点多余。毕竟,我只是用这个扫描一下。于是,决定自己用C#做个GUI版,模仿实现一下。 然后我看了一下目录结构。如下图所知 貌似作者只是简单写了。然后对应着调用扫描器。我想了下,可以通过修改文件名来实现,因为wwwscan默认只能识别cgi.list,那么我想的是当我选中一种扫描类型后。将对应的字典,比如asp.list改成cgi.list。当然为了保护原文件,复制。。然后调用。这样就不用有这么多exe了。。 第二个我想实现作者的检测网站的脚本类型的功能,想到了两种方法。一种是循环访问index.xxx文件,xxx对asp jsp等,然后判断http状态码,200的话就可以对应判断出来网站脚本了。但是这样测试了一会没成功,还是算了,,然后想了一种猥琐流的方法,就是直接访问首页。然后把源码下载下来,然后搜索".xxx"字符串,找到就行了,,当然这两种方法都是不完善的。。我也没想到什么完美的。。希望有人知道的话留言指教。 最后的目录就清爽多了 那个4p啊,,就是盲扫描了。。不知道网站脚本的时候采用。。。 界面基本完全模仿了作者的UI,进行了略微调整,希望作者不介意。 程序需要.net framework 3.5。。win7默认都有。不用担心。xp用户可以下载环境后使用。毕竟是扫描工具,国内那些2b杀软会报毒,不放心的去世界杀毒网自己扫描吧。千万不要用来干坏事,遵守我国相关法律法规。 [downloadicon href=http://pan.baidu.com/share/link?shareid=83803&uk=1493685990]WebScan下载[/downloadicon]

2012-04-18 · 1 min · bystander