HarmonyOS 7 Image Kit 图像编码专题:TIFF 打包、PixelMap 格式与大图内存控制
同样一张图,文件只有 3MB,解码以后内存占了几十 MB——这就是像素格式没搞对。

做图片处理功能的时候,最开始的思路很简单:拿一张图,解码成 PixelMap,处理完再编码存出去。
跑大图的时候出问题了:4000×3000 的图,文件大小才 3MB,解码完内存直接涨了几十 MB。手机差点直接 OOM。
这时候才意识到:图片文件大小和内存占用完全是两回事。文件是压缩的,解码以后是原始像素,每个像素都要占内存。
一、先算一笔账
拿一张 4000×3000 的图片算一下:
| 项目 | 数值 |
|---|---|
| 图片文件大小 | 约 3MB(JPEG 压缩) |
| 解码后像素数 | 4000 × 3000 = 1200 万像素 |
| 每像素字节数 | 4 字节(ARGB_8888) |
| 内存占用 | 1200 万 × 4 = 48MB |
看到了吧?文件才 3MB,解码以后内存直接 48MB。这还是正常情况。如果是 RGBA_F16 格式,每像素 8 字节,直接 96MB。
很多人只看文件大小判断内存,这是错的。文件大小是压缩后的,内存是解码后的原始像素。
二、像素格式为什么重要
不同的像素格式,每个像素占的字节数不一样。
| 格式 | 每像素字节 | 用途 |
|---|---|---|
| RGB_565 | 2 | 不透明图片,省内存 |
| ARGB_8888 | 4 | 通用,带透明通道 |
| Y8 | 1 | 灰度图,黑白照片 |
| ALPHA_F16 | 8 | 高精度透明通道 |
很多人不管什么图都用 ARGB_8888。灰度图也用这个格式,那就是浪费——明明 1 字节就能存的灰度,用了 4 字节。
这段代码解决什么问题: 指定像素格式解码图片。
文件: image/ImageDecoder.ets
用途: 控制解码内存
接入位置: 图片加载逻辑
import image from '@ohos.multimedia.image';
async decodeGrayImage(buf: ArrayBuffer) {
const src = image.createImageSource(buf);
const opts: image.DecodingOptions = {
desiredPixelFormat: image.PixelMapFormat.Y8
};
const pixelMap = await src.createPixelMap(opts);
return pixelMap;
}
灰度图就用 Y8 格式,内存直接少了 3/4。
三、大图为什么要降采样
很多人做大图的时候,直接整张解码。4000×3000 的图,解码完 48MB,然后缩放成 800×600 显示。那前面解码的 48MB 不就浪费了吗?
正确的做法是:解码的时候就降采样。直接解码成你需要的尺寸,不要解码成原始尺寸再缩放。
| 错误做法 | 正确做法 |
|---|---|
| 解码原图 → 缩放 | 解码时直接降采样到目标尺寸 |
| 内存 48MB | 内存 2MB |
差了几十倍。
四、TIFF 打包和普通 JPEG 有什么不一样
API 26 的 Image Kit 新增了 TIFF 打包配置。很多人把 TIFF 参数照搬 JPEG 的思路,这不对。
| 对比 | JPEG | TIFF |
|---|---|---|
| 压缩方式 | 有损 | 无损/多种压缩 |
| 像素格式 | 有限 | 支持更多格式(Y8、ALPHA_F16) |
| 适用场景 | 照片 | 扫描件、高精度图 |
| 大小控制 | 质量参数 | PackingSizeLimit |
TIFF 适合存扫描件、灰度图这种高精度的东西。不要用 TIFF 存普通照片,文件会大很多。

五、编码完的 PixelMap 为什么要释放
还有个容易踩的坑:编码完了,PixelMap 不释放。
很多人写完图片就不管了,内存一直占着。处理几张大图,内存直接爆了。
正确的做法是:编码完之后,立刻 release PixelMap。
六、方向信息为什么会导致图片旋转
还有个反常识的坑:图片方向。
手机拍的照片,方向信息存在 EXIF 里。图片本身的像素可能是横的,EXIF 里记录了"要旋转 90 度显示"。
很多人处理图片的时候,只解码像素,不管 EXIF。结果就是:拍的竖照片,存出去变成横的了。
正确的做法是:处理图片的时候,把 EXIF 里的方向信息也考虑进去。
七、几个容易踩的坑
第一个坑:只看图片文件 KB 数判断内存。文件小不代表内存小。
第二个坑:大图直接整张解码。不解采样,内存直接爆。
第三个坑:忽略像素格式的 Bytes Per Pixel。灰度图也用 ARGB_8888,浪费内存。
第四个坑:编码完成后 PixelMap 不释放。处理几张图,内存涨几十 MB。
第五个坑:TIFF 参数照搬 JPEG 思路。TIFF 有自己的配置,不能直接套。

这次做图片处理最大的体会是:图片内存不是看文件大小,是看像素数乘以每像素字节数。选对格式、做好降采样、用完及时释放,这三点做好了,大图内存问题就解决了一大半。
更多推荐

所有评论(0)