QT4平台下使用QImage进行图像加载时的一个坑儿,测试了一个小时才发现问题之所在,录于此,以备忘之,共享于众,警于后人。

技术背景

生产环境如下:

  1. VS2010
  2. QT 4.8.5
  3. OGRE 1.7.4

使用C++将QT与OGRE融合在一起做三维场景仿真,OGRE渲染出图像序列,QT对保存到硬盘上的图像序列进行再加工,生成gif动图。背景介绍完毕。

坑之所在

OGRE保存的图像以jpg或png为格式,通过以下代码即可实现带时间戳的图像保存:

1
writeContentsToTimestampedFile("shots-",".jpg");

以上函数是RenderWindow的一个公开成员函数,第一个参数是保存图像文件名的前缀,第二个参数是后缀,也是用来标识以什么图像格式进行保存。

在OGRE渲染的的帧间进行保存,即可生成一个图像序列,并保存到硬盘上,默认是在程序运行的当前目录。

由于OGRE在渲染的同时将图像保存到硬盘中,而不是保存到内存中,否则渲染时间一长再强的计算机也会“爆掉”。所以在保存到硬盘之后,QT需要从硬盘中将单幅图像数据读到内存中。如果使用QT的QImage来加载图像的话,就需要注意一个图像api接口的问题:

QT4用的图像格式与OGRE1.7的不完全一样,OGRE保存的图像直接用QImage的加载函数是无法加载的!

两种解决途径:

  1. 修改一方的图像api,使保存方和打开方使用的图像库一致。好在OGRE和QT都是开源的,能够定位到使用的图像库,比如libjpeg.lib等。但是还是相当耗时费力的。
  2. 保存成其他格式,经过测试jpg格式的图像两者是不兼容的,而png兼容的比较好一些。简单的作法就是OGRE输出的图像都保存为png格式,QImage加载的时候也指定png格式加载,完成。

最后,还需要说明的一点QT中从硬盘加载图像可以使用多种方法,首先要对QImage、QPixmap、QBitmap、QPicture四者的区别有一个基本概念,这里不赘述:

  • QPixmap主要是用于绘图,针对屏幕显示而最佳化设计。
  • QImage主要是为图像I/O、图片访问和像素修改而设计的。
  • QBitmap是QPixmap的一个子类,主要用于显示单色位图。
  • QPicture则是一个绘图装置,用于记录和重播Qpainter的绘图指令。

附一小段图像加载与基本处理的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
QImage *img;//原图像
//省略从硬盘加载图像代码
QImage *grayImg;//处理后灰度图像
int width=img->width();//图像宽
int height=img->height();//图像高
int bytePerLine=(width*24+31)/8;//图像每行字节对齐
unsigned char *data=img->bits();//获取图像像素字节数据的首地址
graydata=new unsigned char[bytePerLine*height];//存储处理后的数据

unsigned char r,g,b;
for (int i=0;i<height;i++)
{
for (int j=0;j<width;j++)
{
r = *(data+2);
g = *(data+1);
b = *data;

graydata[i*bytePerLine+j*3] =(r*30+g*59+b*11)/100;
graydata[i*bytePerLine+j*3+1]=(r*30+g*59+b*11)/100;
graydata[i*bytePerLine+j*3+2]=(r*30+g*59+b*11)/100;

data+=4;
}
}
grayImg=new QImage(graydata,width,height,bytePerLine,QImage::Format_RGB888);
//省略将生成的灰度图像保存到硬盘