# 【小白课程】以openKylin看图软件为例,浅谈图片编解码库—FreeImage 看图软件是openKylin操作系统上一款开源的图像查看软件,支持对图片进行基本操作,如:缩放、翻转、详情查看、复制、打印、重命名等,同时还可以对图片进行裁剪、存储、标注和ocr(文字识别)。 ![图片](https://www.openkylin.top/upload/202302/1675302019720987.png) 图1 看图软件界面 作为图像查看软件,查看图片是其基本功能,也是最重要的功能。在看图软件V1.2.0版本中,新增了10种图片格式(exr,psd,jfi,jif,jng,wbmp,xbm,xpm,jp2,j2k)的查看和保存,这些格式在技术上都是通过FreeImage库实现的。下面将为大家着重介绍看图软件中所使用的图片编解码库—FreeImage。 ## 1.看图软件图片编解码库介绍 openKylin系统中的看图软件目前共支持30种图片格式,分别为:bmp,jpeg,jpg,jpe,pnm,pgm,ppm,pbm,sr,ras,dib,png,apng,gif,webp,tga,svg,ico,tiff,tif,exr,psd,jfi,jif,jng,wbmp,xbm,xpm,jp2,j2k。为支持上述图片格式,看图软件使用以下库进行图片编解码:opencv库,FreeImage库,apng库,gif库等。其中,一半格式的图片使用的是大家比较熟悉的opencv库编解码,个别格式的图片,如svg等有自己的相关库。除此之外都是使用的FreeImage库进行图片的读写。在使用过程中,我们发现,对上层应用来说FreeImage库快速便捷,易于使用。 ## 2.FreeImage库介绍 FreeImage库是一款开源的,免费的和跨平台的图片编解码库。支持对20多种流行图形格式的处理,如BMP 、JPEG 、GIF 、PNG 、TIFF等。使用FreeImage库时要安装libfreeimage-dev和libfreeimageplus-dev。同时,FreeImage库提供了很多获取位图信息的接口,具有快速、灵活、简单易用的特点。FreeImage库中的所有函数都是以FreeImage_开头,如图像文件的读写函数分别为FreeImage_Load 和FreeImage_Save ,并且和opencv之间相互转换也很简单,感兴趣的小伙伴可前往FreeImage官网查看更多详情。 ## 3.使用FreeImage库加载图片 看图软件在加载或操作一张图片的整个过程中都是以cv::mat矩阵来存储图片的。打开一张图片时,看图软件使用FreeImage库加载图片的完整流程如下所示: 1. 获取图片真实格式; 2. 判断图片是否支持FreeImage读入; 3. FreeImage加载图片,获得FIBITMAP; 4. 将FIBITMAP转换为cv::mat; 5. 从内存中删除libfreeimage载入的图片,防止内存泄漏。 ### 3.1 获取图片真实格式 在操作图片时,图片后缀可能是.xbm,.sr等,但这并不代表图片是xbm或sr格式,此时需要先通过库函数来获取图片的真实格式。 // 用库获取文件格式,path是图片的路径。QByteArray temp_path; temp_path.append(path.toUtf8()); FREE_IMAGE_FORMAT format = FreeImage_GetFileType(temp_path.data()); FreeImage_GetFileType:由文件头拿到文件类型。参数:图片路径。这个函数的返回值是FREE_IMAGE_FORMAT。可以从下图看到,返回值也可能是FIF_UNKNOWMN。 ![图片](https://www.openkylin.top/upload/202302/1675302047853305.png) 图2 图片类型 如果从库函数中解析出来的文件格式为FIF_UNKNOWMN,我们会从文件数据的角度,通过判断文件头,再次解析图片格式,提升拿到文件格式的正确率。 QFile file(path);if (!file.open(QIODevice::ReadOnly)) {      return FIF_UNKNOWN;}const QByteArray data = file.read(64);/* Check bmp file */if (data.startsWith("BM")) {    s return FIF_BMP;}//path为图片路径 ### 3.2 判断是否支持读入 在拿到文件类型后,加载图片之前,我们还需要做一个工作:判断该格式是否可以被FreeImage库读取。其中FreeImage_FIFSupportsReading用来判断是否支持该位图类型的读操作。 ### 3.3 加载图片 假设我们已经拿到图片的正确格式,且该格式可以被FreeImage库读取。则调用库函数FreeImage_Load 加载位图,返回值为FIBITMAP。FIBITMAP数据结构保存着位图信息和像素数据,是FreeImage的核心。 ### 3.4 将FIBITMAP转换成mat 在看图软件中,读写图片的整个流程的数据都是以cv::mat矩阵进行传递的。之所以使用cv::mat是为了之后能够对看图软件现有的功能进行扩展,尤其是opencv提供的AI方向。 ![图片](https://www.openkylin.top/upload/202302/1675302060527556.png) 图3 看图软件结构 因此,在拿到FreeImage返回的FIBITMAP后,我们需要将它转换为cv::mat。FIBITMAP转换成cv::mat时,首先要看构造一个图片的mat矩阵都需要什么参数。 Mat(int rows, int cols, int type, void* data, size_t step=AUTO_STEP);//行数rows,列数cols,类型type,指向用户数据的指针data,每行占据的字节数。 所以,接下来我们的任务就是,从FIBITMAP拿到需要的所有参数。 1. int rows, int cols 对于行数和列数,FreeImage有函数可以直接调用,得到行列。 FIBITMAP *dib;int width = FreeImage_GetWidth(dib);int height = FreeImage_GetHeight(dib); 2. inttype对于type,需要稍微做一下处理。FreeImage库提供了查看图片深度和数据类型的方法。 int bpp = FreeImage_GetBPP(src);//图片的深度FREE_IMAGE_TYPE fit = FreeImage_GetImageType(src);//返回位图的数据类型 拿到FreeImage的类型的枚举值后,一一对应转为cv::mat的数据类型即可。 ![图片](https://www.openkylin.top/upload/202302/1675302073529292.png) 图4 数据类型 3. void* data指向用户数据的指针data。 库中同样有可以直接调用的函数。FreeImage_GetBits(FIBITMAP *dib):返回一个指向位图的数据位的指针 4. size_t stepstep 每行占据的字节数FreeImage_GetPitch(FIBITMAP *dib):返回位深度或线宽度(又叫做扫描宽度)。 是以字节为单位返回对齐到下一个32位字节边界的位图宽度。 FIBITMAP *dib;int step = FreeImage_GetPitch(dib); ### 3.5内存释放 FIBITMAP *dib;FreeImage_Unload(dib);//从内存中删除载入图片,防止内存泄漏 除了FreeImage库外,目前还有许多优秀的图片编解码库,openKylin看图软件后续会适配更多的图片库来支持更多格式,并利用opencv的特性来扩展一些特色功能。各位小伙伴敬请期待吧! ![图片](https://www.openkylin.top/upload/202302/1675302088630447.png) openKylin(开放麒麟)社区旨在以“共创”为核心,在开源、自愿、平等、协作的基础上,通过开源、开放的方式与企业构建合作伙伴生态体系,共同打造桌面操作系统顶级社区,推动Linux开源技术及其软硬件生态繁荣发展。社区首批理事成员单位包括麒麟软件、普华基础软件、中科方德、麒麟信安、凝思软件、一铭软件、中兴新支点、元心科技、中国电科32所、技德系统、北京麟卓、先进操作系统创新中心等13家产业同仁和行业机构。 来源:openKylin 审核:openKylin