文档章节

C#中使用FreeImage库加载Bmp、JPG、PNG、PCX、TGA、PSD等25种格式的图像(源码)。

abcijkxyz
 abcijkxyz
发布于 2016/11/22 16:39
字数 1833
阅读 84
收藏 0

  其实我一直都是喜欢自己去做图像格式的解码的(目前我自己解码的图像格式大概有15种),但是写本文主要原因是基于CSDN的这个帖子的:                             

      http://bbs.csdn.net/topics/390510431     用pictureBox显示一个黑白8bit图像,如何消除颗粒感

      用于测试的原始的JPG图像: http://files.cnblogs.com/Imageshop/img01.rar

      这个帖子中,作者的需要加载一副灰度的8位的PG格式图像,但是利用.net的Bitmap类加载的图像会出现明显颗粒感,由于.net中的Bitmap类是基于GDI+操作的,因此我也是试着用我的Imageshop打开这幅图像(Imageshop内部也是用GDI+的API实现的),同样有颗粒感。因此,我们需要从其他的手段来解决这个问题。

                            

              .net下加载的效果                                                         Photoshop打开的效果

      首先,我用了VS6.0中的Stdpicture对象来加载这幅图像,能得到正确的结果。然后用PS打开它,也能得到较好的效果,最后用微软的图片查看器,也是可以正确显示的。最后用mspaint(画图)工具打开,则出现了和在.net中一样的效果。

      因此,我们的第一理想方案是使用com里的Stdpicture来解决这个问题,其实在VB6.0下,一个LoadPicture函数就可以解决它,但是在C#下要使用它,需要很多API函数来处理,我自己试着搞了下,觉得过于繁琐,因此放弃了。

      因此,我把希望投向了比较有名的图像解码的软件FreeImage中,经过试验,发现FreeImage的解码是和PS一致的

     我们先来看看百度对FreeImage的介绍:

      FreeImage是一款免费的、开源的、跨平台(Windows 、Linux 和Mac OS X )的,支持20 多种图像类型的(如BMP 、JPEG 、GIF 、PNG 、TIFF 等)图像处理库。其最大优点就是采用插件驱动型架构,具有快速、灵活、简单易用的特点,得到了广泛使用。
      FreeImage 的主要功能有多格式位图的读写;方便的位图信息获取;位深度转换;位图页面访问;基本的几何变换和点处理;通道合并与混合等。FreeImage 暂时不支持矢量图形和高级图像处理,位图绘制需要由用户来完成。
      FreeImage 中的所有函数都以FreeImage_ 开头,如图像文件的读写函数分别为FreeImage_Load 和FreeImage_Save 。FIBITMAP 数据结构保存着位图信息和像素数据,是FreeImage 的核心。

     由上述可见,FreeImage的侧重点偏向于图像的解码和编码,显示图像则需要用户自己负责,而这正是我们所需要的。

     为了能在.NET中使用FreeImage,我知道的有两种方式,一种是直接使用FreeImage 的Flat API,而这需要对使用的API函数进行声明。另外一种方式就是使用FreeImage 提供的FreeImageNET.dll中提供的类库(其实就是对FreeImage.dll中函数的封装)。 我这里把两种方式的实现都简单的描述下:

public static Bitmap LoadImageFormFreeImage(string FileName)
        {
            Bitmap Bmp = null;
            FREE_IMAGE_FORMAT fif = FREE_IMAGE_FORMAT.FIF_UNKNOWN; ;
            fif = FreeImage_GetFileType(FileName, 0);
            if (fif == FREE_IMAGE_FORMAT.FIF_UNKNOWN)
            {
                fif = FreeImage_GetFIFFromFilename(FileName);
            }

            if ((fif != FREE_IMAGE_FORMAT.FIF_UNKNOWN) && (FreeImage_FIFSupportsReading(fif) != 0))
            {
                IntPtr Dib = FreeImage_Load(fif, FileName, 0);
                int Bpp = FreeImage_GetBPP(Dib);
                PixelFormat PF;
                int Width, Height, Stride;
                switch (Bpp)
                {
                    case 1:
                        PF = PixelFormat.Format1bppIndexed; break;
                    case 4:
                        PF = PixelFormat.Format4bppIndexed; break;
                    case 8:
                        PF = PixelFormat.Format8bppIndexed; break;
                    case 16:
                        PF = PixelFormat.Format16bppRgb555; break;
                    case 24:
                        PF = PixelFormat.Format24bppRgb; break;
                    case 32:
                        PF = PixelFormat.Format32bppArgb; break;
                    default:
                        FreeImage_Free(Dib);
                        return null;
                }
                Width = FreeImage_GetWidth(Dib);                        //  图像宽度
                Height = FreeImage_GetHeight(Dib);                      //  图像高度
                Stride = FreeImage_GetPitch(Dib);                       //  图像扫描行的大小,必然是4的整数倍

                /**  方案1:存在内存泄露
                    *  FreeImage_FlipVertical(Dib);                        // 因为FreeImage的认为的图像的起点在左下角,不进行翻转则图像的倒过来的
                    *  IntPtr Bits = FreeImage_GetBits(Dib);               // 得到图像数据在内存中的地址
                    *  Bmp = new Bitmap(Width, Height, Stride, PF, Bits);  // 实际上调用的GdipCreateBitmapFromScan0函数从内存创建位图
                **/


                //方案2:
                IntPtr Bits = FreeImage_GetBits(Dib);
                Bmp = new Bitmap(Width, Height, Stride, PF, Bits);
                Bmp.RotateFlip(RotateFlipType.RotateNoneFlipY);         // 调用GDI+自己的旋转函数
                if (Bpp <= 8)
                {
                    ColorPalette Pal = Bmp.Palette;                     //  设置调色板
                    RGBQUAD* GdiPal = FreeImage_GetPalette(Dib);
                    int ClrUsed = FreeImage_GetColorsUsed(Dib);
                    for (int I = 0; I < ClrUsed; I++)
                    {
                        Pal.Entries[I] = Color.FromArgb(255, GdiPal[I].Red, GdiPal[I].Green, GdiPal[I].Blue);
                    }
                    Bmp.Palette = Pal;
                }
                FreeImage_Free(Dib);                                        // 使用方案2则可以立马释放
                return Bmp;
            }
            return null;
        }
    }

 

  上述代码中,我们对方案1为什么存在内存泄露做一定的说明。

      方案1中,Bmp = new Bitmap(Width, Height, Stride, PF, Bits)这条语句实际上调用了GDI+的函数GdipCreateBitmapFromScan0从内存创建位图,通过此种方式创建的位图并没有新分配一块内存给创建的位图,而是和Bits对应的内存绑定的。您可以用如下的代码验证这一点:

BitmapData  BmpData = Bmp.LockBits(new Rectangle(0, 0, Bmp.Width, Bmp.Height), ImageLockMode.ReadWrite, Bmp.PixelFormat);
 if (BmpData.Scan0 == Bits ) 
        MessageBox.Show ("通过GDI+创建的图像和FreeImage的DIB对象公用同一内存.")
 Bmp.UnlockBits(BmpData);

  正是由于这个原因的存在,如果采用方案1,我们不能在创建GDI+的位图后立马释放FreeImage的创建的DIB对象,即不能调用FreeImage_Free(Dib);如果调用了,对应的 Bmp对象实际上是个空对象了。

     这样的话也许可能没有关系,我们只要在适当的地方调用Bmp.Dispose,不就可以了吗,你可以做个试验,使用这段代码,然后不断的打开新图像,你会发现程序占用的内存会不断的增加,而没有释放。因此,我们看看MSDN对GdipCreateBitmapFromScan0这个函数是怎么解释的,特别是最后一个参数:

scan0 [in]

Type: BYTE*

Pointer to an array of bytes that contains the pixel data. The caller is responsible for allocating and freeing the block of memory pointed to by this parameter. 

     上述文字表示用户需要对分配的内存进行释放,也就是说Dispose方法无法释放该部分内存。

     有了上述的问题,我们转而使用方案2,方案2使用了一句Bmp.RotateFlip(RotateFlipType.RotateNoneFlipY);这个语句会创建一副新的位图,也就是说进行旋转后的图像已经不再同FreeImage使用同一块内存了。那么此时就可以放心的释放掉FreeImage的DIB对象了。

     本以为RotateFlip函数会降低效率,测试表面微软对这个函数的执行效率还是很高的,其实这个函数的函数完全可以借助于CopyMemory函数来高速实现。

  当图像的位深小于8时,需要获取调色板的数据。但是我对认为上述获取调色板的FreeImage_GetPalette函数存在内存泄露,无法释放这些RGBQUAD*分配的内存的。FreeImage应该考虑使用类似于GDI+中获取调色板数据那种方式。

  使用FreeImageNET.dll中提供的类库,则编写代码更为方便,推荐使用第二种方式,朋友们可以参考附件。

  实际上FreeImage还有很多强大的功能,比如色深转换、充分利用它洗看图软件,格式批处理那是很快捷方便的。

  附件中的拖动图像的方式我认为也是值得作为大家学习的。

     http://files.cnblogs.com/Imageshop/FreeImage.rar

     http://files.cnblogs.com/Imageshop/FreeImageApi.rar

 

 

 ***************************作者: laviewpbt   时间: 2013.7.7   联系QQ:  33184777  转载请保留本行信息*************************

 

本文转载自:http://www.cnblogs.com/Imageshop/p/3176478.html

共有 人打赏支持
abcijkxyz
粉丝 63
博文 6196
码字总数 1876
作品 0
深圳
项目经理
私信 提问
Building OpenCascade on Windows with Visual Studio

Building OpenCascade on Windows with Visual Studio eryar@163.com 摘要Abstract:详细说明OpenCascade的编译配置过程,希望对你编译OpenCascacde有所帮助。本文内容来自OCCT的Overview文档...

eryar
2013/09/23
0
0
图像编程的好帮手-四大图像库:OpenCV/FreeImage/CImg/CxImage

图像编程的好帮手-四大图像库:OpenCV/FreeImage/CImg/CxImage 2009-01-09 20:22 1.对OpenCV 的印象:功能十分的强大,而且支持目前先进的图像处理技术,体系十分完善,操作手册很详细,手册...

晨曦之光
2012/05/28
687
0
Python 图像处理库 Mahotas 0.7 发布

Mahotas 是一个 Python 的图像处理库,包含大量的图像处理算法,使用 C++ 实现的算法,处理性能相当好。 新版本删除对 scipy 的依赖,增加了一个 interpolate模块,修复了 64 位系统下 Free...

索隆
2011/12/12
0
0
freeimage和cximage二次开发

最近正在进行数字摄影测量,需要进行图像、图形处理,比如三维转化为二维的图形处理等。要进行二次开发一个图形转换系统,大家觉得freeimage和cximage哪个更加适合二次开发的? 我是一名新手...

天涯明月
2013/03/04
1K
2
Mahotas 0.5.1 发布,Python图像处理

Mahotas 是一个 Python 的图像处理库,包含大量的图像处理算法,使用 C++ 实现的算法,处理性能相当好。 该版本对 freeimage 的依赖变成可选;扩展了 bbox() 以支持 N>2 维;完善了文档。...

红薯
2010/09/14
166
0

没有更多内容

加载失败,请刷新页面

加载更多

C++ vector和list的区别

1.vector数据结构 vector和数组类似,拥有一段连续的内存空间,并且起始地址不变。 因此能高效的进行随机存取,时间复杂度为o(1); 但因为内存空间是连续的,所以在进行插入和删除操作时,会造...

shzwork
今天
3
0
Spring之invokeBeanFactoryPostProcessors详解

Spring的refresh的invokeBeanFactoryPostProcessors,就是调用所有注册的、原始的BeanFactoryPostProcessor。 相关源码 public static void invokeBeanFactoryPostProcessors(Configu......

cregu
昨天
4
0
ibmcom/db2express-c_docker官方使用文档

(DEPRECIATED) Please check DB2 Developer-C Edition for the replacement. What is IBM DB2 Express-C ? ``IBM DB2 Express-C``` is the no-charge community edition of DB2 server, a si......

BG2KNT
昨天
3
0
Ubuntu 18.04.2 LTS nvidia-docker2 : 依赖: docker-ce (= 5:18.09.0~3-0~ubuntu-bionic)

平台:Ubuntu 18.04.2 LTS nvidia-docker2 版本:2.0.3 错误描述:在安装nvidia-docker2的时候报dpkg依赖错误 nvidia-docker2 : 依赖: docker-ce (= 5:18.09.0~3-0~ubuntu-bionic) 先看一下依......

Pulsar-V
昨天
4
0
学习笔记1-goland结构体(struct)

写在前面:若有侵权,请发邮件by.su@qq.com告知。 转载者告知:如果本文被转载,但凡涉及到侵权相关事宜,转载者需负责。请知悉! 本文永久更新地址:https://my.oschina.net/bysu/blog/3036...

不最醉不龟归
昨天
4
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部