闲来玩玩图像处理,拿破仑说过:“不想自己实现滤镜的美工不是好程序员~~#@!*^...#&!@......”  因为在学校做过很多美工的工作,而且从小就喜欢画画所以对图像相关的东西都还比较感兴趣,而且PS提供了强大的功能,那就是自己写的滤镜程序可以以适当的形式嵌入作为滤镜库里的一种效果而存在,要是能自己能写常用的滤镜效果以后用起来就方便多了。从最简单的bitmap开始,bitmap是Windows系统下的标准图像格式。由于位图不采用任何压缩方法,所以大小一般都比较大。图像的结构可以表示如下:

1.首先是位图文件头

  两字节的位图文件类型用于指示位图,其值必须为0x4d42,即"BM",接着是4个字节存储的位图大小,之后的4个字节保用不用,最后是记录位图数据距离位图文件头的偏移量。

2.位图信息头

  内容太多就不一一介绍了。

  如果是24位位图,是指一个像素的颜色由24bit来决定,24bit中R、G、B三原色各占8bit,当然也有32bit位图,就是多了一个α分量,这个分量对于调节颜色的透明度很重要。24位的位图在位图信息头后面紧接着的就是位图数据,而对于8位、16位的位图,位图信息头后还有一个颜色块,用于说明各个颜色分量的亮度。固然我们可以使用MFC或者其他一些类库提供的编程接口来完成位图解析与处理的工作,但不从头实现一遍总会觉得失去了什么,而且别人的实现方式你怎么知道?现以C语言方式实现位图的读写与几个小滤镜效果。

  首先在bitmap.h中定义需要的数据结构:

  1. #pragma pack(1)
  2.  
  3. typedef unsigned char BYTE;
  4.  
  5. typedef unsigned short WORD;
  6.  
  7. typedef unsigned long DWORD;
  8.  
  9. typedef long LONG;
  10.  
  11. typedef struct bitmap_filehead{
  12. WORD bfType;
  13. DWORD bfSize;
  14. WORD bfReserved1;
  15. WORD bfReserved2;
  16. DWORD bfOffset;
  17. }BITMAPFILEHEADER;
  18.  
  19. typedef struct bitmap_infohead{
  20. DWORD biSize;
  21. LONG biWidth;
  22. LONG biHeight;
  23. WORD biPlanes;
  24. WORD biBitCount;
  25. DWORD biCompression;
  26. DWORD biSizeImage;
  27. LONG biXPelsPerMeter;
  28. LONG biYPelsPerMeter;
  29. DWORD biclrUsed;
  30. DWORD biclrImportant;
  31. }BITMAPINFOHEADER;
  32.  
  33. typedef struct rgb_quad{
  34. BYTE rgb_red;
  35. BYTE rgb_green;
  36. BYTE rgb_blue;
  37. BYTE rgb_Reserved;
  38. }RGBQUAD;

bitmap.h

  在位图处理的时候需要注意一些问题,比如说每行的字节数必须是整数,所以下面用到的lineByte的计算方式应该是这样:

  1. int lineByte = (bmpWidth*biBitCount/+)/*;
  2. //每行的字节数必须为4个倍数,bmpWidth为宽度,biBitCount为每个像素所占位数

lineByte

  

  下面来看第一个滤镜:灰度效果。所谓灰度,无非是所有像素颜色介于黑白之间。因为纯黑色的表示是RGB(0,0,0),而纯白色则是RGB(255,255,255),所以要保证颜色从黑到白变化(0-255)就只能让RGB三个分量一致,所以灰度滤镜的公式为X=(R+G+B)/3,然后只需要将原图像的24位颜色数据全部赋值成X就行了。代码如下,pBmpBuf是用于存放位图数据的缓冲区。

  1. //灰度处理
  2. bool imageGray(unsigned char *pBmpBuf,int lineByte)
  3. {
  4. if(pBmpBuf==NULL)
  5. return false;
  6.  
  7. for(int i=;i<lineByte*bmpHeight/;i++)
  8. {
  9. pBmpBuf[*i] = (pBmpBuf[*i]+pBmpBuf[*i+]+pBmpBuf[*i+])/;
  10. pBmpBuf[*i+] = pBmpBuf[*i];
  11. pBmpBuf[*i+] = pBmpBuf[*i];
  12. }
  13. return true;
  14. }

灰度滤镜

  貌似博客园不能上传bmp格式的图片,所以我把图片转成png格式的了,来看一下效果:

  

  

  第二个滤镜:黑白效果。顾名思义也就是图像只能有黑白两种颜色,而这个滤镜有点特殊,那就是根据什么来判断一个像素点到底应该是黑还是白,感性的认识是原来较深的地方应该用黑色,较浅的地方应该用白色。那么深和浅又怎么区分呢?这个就要按照你需要达到的效果来定义了,也就是说这个阈值是自定义的,甚至都可以做成一个滑动条来改变。我在这里是将原像素颜色计算得到的灰度值与100进行比较,如果大于等于100则为白色,反之则为黑色,代码如下:

  1. bool imageBlackWhite(unsigned char *pBmpBuf,int lineByte)
  2. {
  3. if(pBmpBuf==NULL)
  4. return false;
  5.  
  6. unsigned char temp;
  7. for(int i=;i<lineByte*bmpHeight/;i++)
  8. {
  9. temp = (pBmpBuf[*i]+pBmpBuf[*i+]+pBmpBuf[*i+])/;
  10. if(temp>=)
  11. {
  12. pBmpBuf[*i] = ;
  13. pBmpBuf[*i+] = ;
  14. pBmpBuf[*i+] = ;
  15. }
  16. else
  17. {
  18. pBmpBuf[*i] = ;
  19. pBmpBuf[*i+] = ;
  20. pBmpBuf[*i+] = ;
  21. }
  22. }
  23. return true;
  24. }

黑白滤镜

  效果如下:

   

  

  边缘查找其实是一个很复杂的效果,往深里做可以涉及到滤波器设计等,在这里从简,就按照最简单的想法,用像素的原始颜色数据减去柔化处理后的数据,得到的就是边缘,柔化的方法见底部。

  1. bool imageEdge(unsigned char *pBmpBuf,int lineByte,unsigned long totalbytes)
  2. {
  3. if(pBmpBuf==NULL)
  4. return false;
  5.  
  6. tempbuf = (unsigned char *)malloc(totalbytes);
  7.  
  8. memcpy(tempbuf,pBmpBuf,totalbytes);
  9.  
  10. for(int j=;j<=bmpHeight-;j++)
  11. {
  12. for(int i=;i<=bmpWidth-;i++)
  13. {
  14. pBmpBuf[j*lineByte+*i] =( pBmpBuf[(j-)*lineByte+*(i-)]/ + pBmpBuf[(j-)*lineByte+*i]/ + pBmpBuf[(j-)*lineByte+*(i+)]/+ \
  15. pBmpBuf[j*lineByte+*(i-)]/ + pBmpBuf[j*lineByte+*(i+)]/ + pBmpBuf[(j+)*lineByte+*(i-)]/ + \
  16. pBmpBuf[(j+)*lineByte+*i]/ + pBmpBuf[(j+)*lineByte+*(i+)]/);
  17.  
  18. pBmpBuf[j*lineByte+*i+] =( pBmpBuf[(j-)*lineByte+*(i-)+]/ + pBmpBuf[(j-)*lineByte+*i+]/ + pBmpBuf[(j-)*lineByte+*(i+)+]/+ \
  19. pBmpBuf[j*lineByte+*(i-)+]/ + pBmpBuf[j*lineByte+*(i+)+]/ + pBmpBuf[(j+)*lineByte+*(i-)+]/ + \
  20. pBmpBuf[(j+)*lineByte+*i+]/ + pBmpBuf[(j+)*lineByte+*(i+)+]/);
  21.  
  22. pBmpBuf[j*lineByte+*i+] =( pBmpBuf[(j-)*lineByte+*(i-)+]/ + pBmpBuf[(j-)*lineByte+*i+]/ + pBmpBuf[(j-)*lineByte+*(i+)+]/+ \
  23. pBmpBuf[j*lineByte+*(i-)+]/ + pBmpBuf[j*lineByte+*(i+)+]/ + pBmpBuf[(j+)*lineByte+*(i-)+]/ + \
  24. pBmpBuf[(j+)*lineByte+*i+]/ + pBmpBuf[(j+)*lineByte+*(i+)+]/);
  25. }
  26. }
  27.  
  28. for(int k=;k<lineByte*bmpHeight/;k++)
  29. {
  30. pBmpBuf[*k] = tempbuf[*k] - pBmpBuf[*k];
  31. pBmpBuf[*k+] = tempbuf[*k+] - pBmpBuf[*k+];
  32. pBmpBuf[*k+] = tempbuf[*k+] - pBmpBuf[*k+];
  33. }
  34. return true;
  35. }

边缘查找

  效果如下,由于只是很简单的边缘查找算法,所以效果不是太好,但人物轮廓还是很明显的:

  

  

  反相效果就是底片效果,没什么好说的,对于像素的各个颜色分量对255求补,即R = 255-R,G = 255-G,B = 255-B。

  1. bool imageReverse(unsigned char *pBmpBuf,int lineByte)
  2. {
  3. if(pBmpBuf==NULL)
  4. return false;
  5.  
  6. for(int i=;i<lineByte*bmpHeight/;i++)
  7. {
  8. pBmpBuf[*i] = -pBmpBuf[*i];
  9. pBmpBuf[*i+] = -pBmpBuf[*i+];
  10. pBmpBuf[*i+] = -pBmpBuf[*i+];
  11. }
  12. return true;
  13. }

反相

  效果如下:

  

  

  柔化效果,这里采用取自身和周围8个像素点的颜色平均值来取代原像素的值,这样能够比较粗略的消除一些噪点,使得图像能够更加平滑,对于噪点比较多的图像效果就是图像更柔和了,而对于已经比较清晰的图像,相应给人的感觉就是清晰度变低了。

  1. bool imageSoft(unsigned char *pBmpBuf,int lineByte)
  2. {
  3. if(pBmpBuf==NULL)
  4. return false;
  5.  
  6. //不用正确的数据结构存储,数据处理起来就是这么费劲
  7. for(int j=;j<=bmpHeight-;j++)
  8. {
  9. for(int i=;i<=bmpWidth-;i++)
  10. {
  11. pBmpBuf[j*lineByte+*i] =( pBmpBuf[(j-)*lineByte+*(i-)]/ + pBmpBuf[(j-)*lineByte+*i]/ + pBmpBuf[(j-)*lineByte+*(i+)]/+ \
  12. pBmpBuf[j*lineByte+*(i-)]/ + pBmpBuf[j*lineByte+*i]/ + pBmpBuf[j*lineByte+*(i+)]/ + pBmpBuf[(j+)*lineByte+*(i-)]/ + \
  13. pBmpBuf[(j+)*lineByte+*i]/ + pBmpBuf[(j+)*lineByte+*(i+)]/);
  14.  
  15. pBmpBuf[j*lineByte+*i+] =( pBmpBuf[(j-)*lineByte+*(i-)+]/ + pBmpBuf[(j-)*lineByte+*i+]/ + pBmpBuf[(j-)*lineByte+*(i+)+]/+ \
  16. pBmpBuf[j*lineByte+*(i-)+]/ + pBmpBuf[j*lineByte+*i+]/ + pBmpBuf[j*lineByte+*(i+)+]/ + pBmpBuf[(j+)*lineByte+*(i-)+]/ + \
  17. pBmpBuf[(j+)*lineByte+*i+]/ + pBmpBuf[(j+)*lineByte+*(i+)+]/);
  18.  
  19. pBmpBuf[j*lineByte+*i+] =( pBmpBuf[(j-)*lineByte+*(i-)+]/ + pBmpBuf[(j-)*lineByte+*i+]/ + pBmpBuf[(j-)*lineByte+*(i+)+]/+ \
  20. pBmpBuf[j*lineByte+*(i-)+]/ + pBmpBuf[j*lineByte+*i+]/ + pBmpBuf[j*lineByte+*(i+)+]/ + pBmpBuf[(j+)*lineByte+*(i-)+]/ + \
  21. pBmpBuf[(j+)*lineByte+*i+]/ + pBmpBuf[(j+)*lineByte+*(i+)+]/);
  22. }
  23. }
  24. return true;
  25. }

柔化

  效果如下,注意左边是原图、右边是结果,仔细看还是能看出有差别的,右边模糊一些:

  

  完整实例程序猛击这里

  (注:程序仅处理24位的位图,所以没有颜色块,需要指明需要处理图片的完整路径,或者将所需处理图片放在工程中。结果另存为了copy-yourbitmap.bmp)

Bitmap的读写和几个小儿科的滤镜效果~的更多相关文章

  1. Bitmap的读写

    Bitmap的读写和几个小儿科的滤镜效果~ 闲来玩玩图像处理,拿破仑说过:“不想自己实现滤镜的美工不是好程序员~~#@!*^...#&!@......”  因为在学校做过很多美工的工作,而且从 ...

  2. android 内部缓存器(手机自带的存储空间中的当前包文件的路径)

    关于Context中: 1. getCacheDir()方法用于获取/data/data/<application package>/cache目录 2. getFilesDir()方法用 ...

  3. ACache【轻量级的开源缓存框架】

    版权声明:本文为HaiyuKing原创文章,转载请注明出处! 前言 官方介绍 ASimpleCache 是一个为android制定的 轻量级的 开源缓存框架.轻量到只有一个java文件(由十几个类精简 ...

  4. 缓存之 ACache

    1.android缓存的介绍 Android开发本质上就是手机和互联网中的webserver之间进行通信,就必定须要从服务端获取数据.而重复通过网络获取数据是比較耗时的.特别是訪问比較多的时候.会极大 ...

  5. Android轻量级的开源缓存框架ASimpleCache

    点击查看原文 先上方法调用,写最经常使用的.其它不一一写 保存数据: ACache mACache=ACache.get(this); mACache.put("数据名称", js ...

  6. C#读写BitMap及颜色相乘

    C#读写BitMap及颜色相乘 private Bitmap ReadBitMapAndMultipy(Bitmap bitmap0) { int x1width = bitmap0.Width; i ...

  7. VB6 GDI+ 入门教程[9] Bitmap魔法(2):数据读写

    本文转自 http://vistaswx.com/blog/article/category/tutorial/page/2 VB6 GDI+ 入门教程[9] Bitmap魔法(2):数据读写 200 ...

  8. Bitmap 图片格式并用 C++ 读写 Bitmap

    转自 Bitmap 图片格式并用 C++ 读写 Bitmap 1.Bitmap 图片格式 每部分的具体内容就不展开了.要说的有两点: (1)调色板不是必须的,可有可无,有没有调色板可以通过位图文件头的 ...

  9. PPM图片格式及其C读写代码

    PPM图像格式介绍 PPM图像格式是由Jef Poskanzer 大叔,在我出生那一年,也就是1991年所创造的,碰巧的是PPM也是天蝎座. PPM(Portable Pixmap Format)还有 ...

随机推荐

  1. word 论文排版 —— 按指定格式章节的自动编号

    在word中如何实现章节标题自动编号 标题样式与标题的编号是两个步骤,为标题建立编号是在为标题样式确定的基础后进行的.这是显而易见的,也即只有先定义了多级标题(也可使用 word 自带的标题样式),才 ...

  2. spring定时任务.线程池,自定义多线程配置

    定时任务及多线程配置xml <?xml version="1.0" encoding="UTF-8"?> <beans xmlns=" ...

  3. React Native细节记录

    1.环境搭建部分 安装完node后建议设置npm镜像以加速后面的过程(或使用***工具).注意:不要使用cnpm!cnpm安装的模块路径比较奇怪,packager不能正常识别! npm config ...

  4. dotnet core 通过修改文件头的方式隐藏控制台窗口

    原文:dotnet core 通过修改文件头的方式隐藏控制台窗口 在带界面的 dotnet core 程序运行的时候就会出现一个控制台窗口,本文告诉大家使用最简单方法去隐藏控制台窗口. 最近在使用 A ...

  5. Arcgis api for javascript学习笔记(4.5版本) - 点击多边形(Polygon)并高亮显示

    在现在的 arcgis_js_v45_api 版本中并没有直接提供点击Polygon对象高亮显示.需要实现如下几个步骤: 1.点击地图时,获取Polygon的Graphic对象: 2.对获取到的Gra ...

  6. Go 在 Windows 上用户图形界面 GUI 解决方案 Go-WinGUI 国产(使用cef 内核)

    Go 在 Windows 上用户图形界面 GUI 解决方案 Go-WinGUI 国产 Go 在服务端的优势不容置疑,但是在桌面应用上却没有好的 GUI 支持,本项目是 Go 语言在 Windows 上 ...

  7. leveldb原理和使用

    LevelDB是一个基于本地文件的存储引擎,非分布式存储引擎,原理基于BigTable(LSM文件树),无索引机制,存储条目为Key-value.适用于保存数据缓存.日志存储.高速缓存等应用,主要是避 ...

  8. .net core实现前后端彻底分离

    问题的关键在跨域 1.我们在services里面 添加跨域内容如下: public void ConfigureServices(IServiceCollection services) { //这个 ...

  9. Delphi 快速获取文件大小(使用_lopen和FileSeek,此函数可以快速获取文件大小,即使文件已经被其它程序锁定)

    function GetFileSize(const fName: AnsiString): Int64; var hFile: THandle; begin hFile := _lopen(PAns ...

  10. latex 常用环境(environment)

    align \begin{align} \overline{A \cup B} &= \overline{A} \cap \overline{B}, \\ \overline{A \cap B ...