多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

CentOS 7.9 源码编译 ImageMagick 支持 HEIC 格式转换完整指南

CentOS 7.9 源码编译 ImageMagick 支持 HEIC 格式转换完整指南 前几天在帮客户处理一批 iPhone 拍摄的照片需求是把服务器上的图片统一转成 HEIC 格式入库服务器是台 CentOS 7.9 的老物理机命令行操作。我第一反应是拿系统自带的 ImageMagick 直接 convert结果一查convert -list format里根本没有 HEIC 这个格式。折腾了两天从编译器到 libde265、x265、libheif 再到 ImageMagick 全部源码重编了一遍总算把转换链路完整跑通。这篇文章就把整套流程原原本本记录下来包括为什么默认装不了、每个依赖的编译顺序、最后怎么验证以及我踩进去又爬出来的几个大坑。给同样需要在这台经典系统上处理 HEIC 图片的同行做个参照。这里默认你已经有一台能正常联网、装好了 CentOS 7.9、拿到 root 权限的机器。关于物理机安装系统、LVM 分区、静态 IP、网卡 bond 这些前置事项本文不展开命令在当前系统上跑通即可物理机虚拟机没有区别。1. 为什么 CentOS 7.9 自带的 ImageMagick 读不了 HEIC1.1 HEIC/HEIF 与 H.265 的关系先弄清楚HEIC 是苹果对 HEIFHigh Efficiency Image File FormatISO/IEC 23008-12容器格式的行业叫法里面的图像数据是拿 HEVC/H.265 编码压缩的。相比 JPEG同等画质下体积通常能小一半上下所以 iPhone 默认拍照格式一换服务器端就不得不面对这种新格式。问题的关键在 H.265 的专利授权上。HEVC 编码器和解码器涉及多个专利池Red Hat 系发行版出于法律和合规考虑默认打包的软件基本不会内置 HEVC/HEIF 支持。CentOS 7.9 仓库里的 ImageMagick 虽然版本不算太老但编译时没有把 libheif 这个外部 delegate 打进包里自然就既读不了也写不了 HEIC。1.2 先检查本机 ImageMagick 到底缺什么登录服务器后先看版本rpm -qa | grep -i imagemagick convert -versionCentOS 7.9 默认仓库给的是 ImageMagick 6.9.x命令是convert不是新版 ImageMagick 7 的magick。接着看它支持哪些格式convert -list format | grep -i -E HEIC|HEIF正常会没有任何输出因为 delegate 列表里根本没这个模块。再确认一下编译时集成了哪些外部库convert -list configure | grep DELEGATES你会看到输出的 delegate 列表里面有 jpeg、png、tiff、webp 这些但就是没有 heic。ImageMagick 本身不是把每种编码器都塞进二进制的而是通过外部库完成具体格式的读写这种机制叫 delegate。HEIC 的 delegate 依赖 libheiflibheif 又依赖 libde265解码和 x265编码所以下面这几步就是从下往上把这些缺口补上。2. 编译环境先把 CentOS 7.9 的旧编译器换掉2.1 安装基础编译工具环境准备阶段先装工具链yum install -y epel-release yum groupinstall -y Development Tools yum install -y wget git automake autoconf libtool pkgconfig cmake nasmgroupinstall Development Tools会把 gcc、make 这些基本工具装齐EPEL 提供 cmake、nasm 等在 Base 仓库里没有或者版本太老的包。这里有个容易忽略的点cmake3和cmake是两个独立的包EPEL 的 cmake3 才是新版本老 cmake 不要指望它。2.2 用 Software Collections 升级 gcc这一步不能省CentOS 7.9 自带的 gcc 是 4.8.5只完整支持 C11而 libheif 新版本已经要求 C14 甚至 C17 了。如果你不去动编译器后面编译 libheif 的时候大概率会碰到一堆语法报错哪怕强行加-stdc14过了编译链接触发的 ABI 问题也够喝一壶。用 Software CollectionsSCL装一个 gcc 9yum install -y centos-release-scl yum install -y devtoolset-9 scl enable devtoolset-9 bash gcc --versionscl enable devtoolset-9 bash这条命令的作用是给当前 shell 环境临时切换编译工具不是永久改系统默认。后面所有编译操作都要在这个 shell 里进行包括 x265、libde265、libheif 和 ImageMagick。如果你开新窗口忘了切gcc 又变回 4.8.5那就全白干了。我习惯在脚本开头直接source /opt/rh/devtoolset-9/enable避免这种低级失误。2.3 cmake 也要换成 cmake3libheif 的 CMakeLists.txt 声明了最低 CMake 版本要求CentOS 7 默认的 cmake 2.8 铁定不够。EPEL 装了 cmake3 之后后面所有cmake命令都用cmake3which cmake3 cmake3 --version如果你懒得每次敲 cmake3也可以alias cmakecmake3但要注意 alias 只在当前 shell 生效脚本里最好还是直接用绝对命令。3. 按顺序编译 x265、libde265、libheif3.1 三者的依赖关系先理清转换 HEIC 是一进一出两条链路读取 HEIC 时 libheif 调用 libde265 解码 H.265 数据写出 HEIC 时调用 x265 把图像数据编码成 H.265再封装成 ISO 容器。所以 libde265 和 x265 是平级依赖都得先于 libheif 编译。libheif 是 ImageMagick 和这两个底库之间的桥必须最后编。3.2 编译 x265x265 源码在 Bitbucket 上直接 clonecd /usr/local/src git clone https://bitbucket.org/multicoreware/x265_git.git cd x265_git/build/linux cmake3 ../../source -DCMAKE_INSTALL_PREFIX/usr/local -DENABLE_SHAREDON -DENABLE_CLION make -j$(nproc) make installnproc是取 CPU 核心数物理机上核心多的话编译能快不少。ENABLE_SHAREDON一定要开后续 libheif 要以动态库方式链接 x265能避免一堆静态库奇奇怪怪的兼容问题。没装 nasm 的话x265 也能编只是少了汇编优化运行速度会打折扣。编译完确认一下x265 --version如果提示找不到libx265.so说明/usr/local/lib还没加进动态库搜索路径这个问题在第 3.5 节一起处理。3.3 编译 libde265libde265 是解码侧依赖用 autotools 那套流程cd /usr/local/src wget https://github.com/strukturag/libde265/archive/refs/tags/v1.0.8.tar.gz tar xzf v1.0.8.tar.gz cd libde265-1.0.8 ./autogen.sh ./configure --prefix/usr/local make -j$(nproc) make install如果./autogen.sh报错说找不到 libtoolize 或 aclocal就是前面 pkgconfig、automake、libtool 没装全回去补装再跑。libde265 对编译器没有 libheif 那么苛刻gcc 4.8 硬编也不是完全不行但既然已经切到 devtoolset-9统一工具链更省事。3.4 编译 libheiflibheif 是核心版本不用追最新选一个和当前系统兼容的稳定版即可我用的是 v1.13.0cd /usr/local/src wget https://github.com/strukturag/libheif/archive/refs/tags/v1.13.0.tar.gz tar xzf v1.13.0.tar.gz cd libheif-1.13.0 mkdir build cd build cmake3 .. -DCMAKE_INSTALL_PREFIX/usr/local -DWITH_X265ON -DWITH_LIBDE265ON -DBUILD_SHARED_LIBSON -DWITH_EXAMPLESOFF make -j$(nproc) make install我试过直接系统 cmake 和默认 gcc 编译这个版本结果在 CMake 检查和编译环节都有问题。切到 cmake3 devtoolset-9 之后一遍过。编译完成后确认 pkg-config 能找到 libheifexport PKG_CONFIG_PATH/usr/local/lib/pkgconfig export LD_LIBRARY_PATH/usr/local/lib pkg-config --list-all | grep -i -E x265|libde265|libheif三条记录都能看到说明依赖关系已经建立。这里导出的环境变量在后续编译 ImageMagick 时同样需要建议保持同一个 shell。3.5 让系统找到 /usr/local/lib 下的库编译完第三方库最容易被忽略的一步是动态库注册。不注册的话后面 ImageMagick 运行时会报找不到共享库echo /usr/local/lib /etc/ld.so.conf.d/local.conf ldconfig再执行ldconfig -p | grep -i -E heif|x265|de265能看到 libheif.so、libx265.so 就是正常的。插一句这一步花了我不少时间第一次编译 ImageMagick 后magick -version都正常一执行转换直接error while loading shared libraries: libheif.so.1就是因为 ldconfig 没配。4. 编译 ImageMagick 并打开 HEIC 支持4.1 解决老版本命令冲突我习惯先把 yum 自带的 ImageMagick 卸载免得命令互相覆盖yum remove -y ImageMagick ImageMagick-libs如果你服务器上有其他脚本依赖系统 ImageMagick卸载前先看清楚没有就放心卸。编译版装到 /usr/local 后convert和magick命令都在 /usr/local/bin 下需要确认 PATH 顺序which convert which magick如果指向 /usr/bin说明 PATH 里 /usr/local/bin 排在后面要么改 PATH要么干脆卸载老包其他办法都容易引出旧命令找旧库的问题。4.2 configure 的关键参数ImageMagick 7 的源码包去官网 archive 目录下载我当时用的是 7.1.0-62cd /usr/local/src wget https://imagemagick.org/archive/releases/ImageMagick-7.1.0-62.tar.xz tar xJf ImageMagick-7.1.0-62.tar.xz cd ImageMagick-7.1.0-62 ./configure --prefix/usr/local --with-heicyes --with-utilitiesyes注意 configure 输出的内容不要直接滑走。找到大概下面这一行HEIC --with-heicyes这里显示 yes说明它已经找到了 libheif。如果显示 no即使你编译完ImageMagick 也不带 HEIC delegate。看到 no 就先别 make直接跳到第 6 节排查。configure 成功之后make -j$(nproc) make install ldconfig4.3 验证 HEIC/HEIF 是否写入 delegate编译完成后先看版本delegate 列表里有没有 heicmagick -version重点看 Delegates 那行应该包含heic。再看支持格式magick -list format | grep -i -E HEIC|HEIF输出大致是HEIC HEIC rw- High Efficiency Image Format (HEIC) HEIF HEIF rw- High Efficiency Image Format (HEIF)左边是格式名中间是读写标志r代表读、w代表写。看到rw-就是完全体如果只有r--或者-w-说明只支持单方向多半是 x265 或 libde265 其中一侧没编上要回到第 3 节检查哪个依赖没被 libheif 找到。5. 实测转换单文件、批量、压缩质量5.1 基础转换命令环境合格后转换命令本身非常简单。ImageMagick 7 用magickmagick input.jpg -quality 85 output.heicHEIC 再转回 JPEGmagick input.heic -quality 90 output.jpg-quality参数在 HEIC 编码里控制的是 H.265 量化质量范围还是 0 到 100我实测下来 85 属于画质和体积都比较平衡的档位。如果源图带了 GPS 或相机型号等 EXIF 元数据需要清掉就在命令里加-stripmagick input.jpg -strip -quality 85 output.heic如果要保留 EXIF就不要加strip。HEIC 容器本身支持 EXIFImageMagick 默认会把源图元数据原样带过去。5.2 批量转换服务器上处理图片批量是刚需。简单 for 循环for f in *.jpg; do magick $f -strip -quality 85 ${f%.jpg}.heic done源文件多、CPU 核心也多的时候可以并行跑用 xargs 控制进程数避免一次把服务器内存吃满find . -maxdepth 1 -name *.jpg -print0 | xargs -0 -P 4 -I{} magick {} -quality 85 {}.heic-P 4是并发数按 CPU 线程数一半以上调都行。我在这台物理机上跑过大概两万张单进程太慢4 并行大概快了 3 倍多服务器负载也没飙到离谱。5.3 体积对比与使用场景提醒我拿一组 4032x3024 的手机实拍图做过对比JPEG quality 90 约 3.2MB转成 HEIC quality 85 约 1.1MBHEIC quality 90 约 1.5MB。数字仅供参考实际和画面噪点、细节复杂程度关系很大纯色天空和密集树叶的体积能差出三倍。整体判断标准是HEIC 比同等质量的 JPEG 通常小 40% 到 60%这也是苹果从 iOS 11 开始用它的核心原因。但注意 HEIC 不是万能格式。转换之前先确认下游消费方支持度iPhone、iPad、macOS 生态没问题新版 Android 和 Windows 装过 HEIF 扩展也能打开但老旧的业务系统、图片服务、浏览器如果没适配转完反而会惹麻烦。我自己处理的那批图最后其实是 HEIC 归档一份、JPEG 再导出一份给人看图存储省了兼容性也没丢。6. 排错实录configure 检测失败和运行时报错6.1 configure 显示 HEIC no 的完整排查链路这个问题会出现在 ImageMagick 的 configure 阶段也是最容易让人懵的地方。原因是 libheif 虽然装了但 pkg-config 没找到。按下面顺序逐步排除pkg-config --modversion libheif没输出版本号就先看 pc 文件在不在ls -l /usr/local/lib/pkgconfig/libheif.pc文件不存在说明 libheif 编译安装有问题回头重编。文件存在但 pkg-config 找不到就是/usr/local/lib/pkgconfig没在 PKG_CONFIG_PATH 里。临时生效export PKG_CONFIG_PATH/usr/local/lib/pkgconfig然后重新跑 ImageMagick 的 configure输出就会从 HEIC no 变成 HEIC yes。这里一定不要用make clean糊弄过去configure 重新执行前把上一次生成的 config.status 也清掉确保检测是全新的。如果 configure 显示 yes但编译时又报heif.h: No such file or directory那是头文件路径的问题。configure 阶段加上./configure --prefix/usr/local --with-heicyes CPPFLAGS-I/usr/local/include LDFLAGS-L/usr/local/lib大多数情况下--prefix/usr/local就能让 ImageMagick 自动找到头文件报这个错多半是某些依赖装到了别的目录或者没按/usr/local作为统一 prefix。6.2 运行时报 NoDecodeDelegate / NoEncodeDelegateForHEIC编译和格式列表都正常但在转换时提示magick: no decode delegate for this image format HEIC magick: no encode delegate for this image format HEIC第一反应看动态库链接ldd /usr/local/bin/magick | grep -i -E heif|x265|de265如果有库显示not found就是 ldconfig 没生效回到第 3.5 节。三个库全部正常再看是不是系统里有多个版本的 libheif 混着。用ldd /usr/local/bin/magick | grep libheif注意解析到的是/usr/local/lib/libheif.so.1而不是/usr/lib64/libheif.so.1。如果混用了不同版本编译的 ImageMagick格式表可能显示 rw-但运行时读取的库版本不一致表现为读写报错不一。办法是重新ldconfig并把无关路径从 PATH 里清干净。6.3 编译器混用导致的 ABI 问题这是必须重点说的一点。x265 和 libheif 如果在不同编译器下编译链接到 ImageMagick 后很容易报undefined reference to std::__cxx11这类 C ABI 错误或者version GLIBCXX_... not found。gcc 4.8 和 devtoolset-9 的 C 标准库 ABI 是有差异的把所有库混在一起编就是埋雷。我踩过一次之后定下规矩从 x265 开始到 ImageMagick全程在同一个scl enable devtoolset-9 bash的 shell 里完成。如果中途混了某个库编到一半发现 gcc 版本不对干脆把那个库的 build 目录删掉重新编别想着只重编一次能补救。尤其 libheif它会把当时检测到的编译器相关信息写进 cmake 缓存缓存不干净后续再折腾都是浪费生命。6.4 实在不想从头编译还有没有捷径如果你短期内只是想把 HEIC 转成 JPEG不追求系统内完整编解码可以直接拿 Docker 跑一个带 HEIC 支持的 ImageMagick 镜像docker run --rm -v $PWD:/work -w /work some-imagemagick-with-heic-image ...但这种方式对生产环境不友好镜像里库的搭法不透明遇到奇奇怪怪的系统调用也不方便排查。另一条路是用官方编译好的静态二进制版本不过静态版是否包含 HEIC delegate 需要逐个 release 确认我见过有人下一个静态包回来照样不支持 HEIC白折腾一场。如果你对这台 CentOS 7.9 有长期依赖我的建议还是老老实实源码编译一遍把环境摸清楚后面升级维护至少知道每一层是什么、在哪里改。这一整套流程跑完其实最大的收获不是最后执行的那条magick命令而是把 HEIC 编码链路从底层弄明白了。以后再遇到 HEIF 格式、Live Photo 里的多帧图像、甚至更冷门的 AVIF需要排查依赖时思路都是同一套确认容器格式、确认编码库、确认 ImageMagick delegate。希望对你有用。
返回列表