libvips Conversion 图像变换模块完全指南格式转换、几何重排与像素混合【免费下载链接】libvipsA fast image processing library with low memory needs.项目地址: https://gitcode.com/gh_mirrors/li/libvips导读libvips/conversion是 libvips 图像处理库中覆盖面最广、日常使用频率最高的功能模块它收录了以某种方式变换图像的全部操作从改变像素格式带格式强制转换、位深搬移、字节序交换、重排几何裁剪、嵌入、翻转、旋转、拼接到波段重组、缓存控制与多图合成。本文以仓库文档 doc/libvips-conversion.md 为骨架结合 libvips/conversion 目录下的 C 源码与 test/test-suite/test_conversion.py 测试用例系统讲解这些操作的分组、参数语义与底层实现原理。读完本文你将能够根据场景正确选择 cast/embed/smartcrop/join/composite 等操作并理解 libvips 惰性求值管线下它们各自的代价与适用边界。模块总览两类截然不同的转换原文档将整个 conversion 模块划分为两大功能组这一划分也直接体现在VipsConversion抽象基类之上格式类转换format改变图像的数据组织方式而不改变像素的空间位置。包括换带格式如 cast 到 32 位无符号整型、实图像与复图像互转、图像与矩阵互转、修改头部字段、波段的重排与折叠等。像素搬移类转换pixel movement把像素在空间上移动。包括翻转、旋转、裁剪、嵌入以及多图的拼接、插入、网格化等。从源码层面看所有 conversion 操作都继承自抽象基类VipsConversion它本身是VipsOperation的子类其唯一的硬性约束是单输入、单输出图像——这一点在 libvips/conversion/pconversion.h 中可以看到结构体里只有一个VipsImage *out输出字段。基类的build方法会预先创建输出图像对象libvips/conversion/conversion.c。模块的注册入口是vips_conversion_operation_init()libvips/conversion/conversion.c它以extern GType 显式调用xxx_get_type()的方式一次性注册了 copy、tilecache、embed、flip、join、cast、bandjoin、composite、addalpha 等 40 余个操作类型。这意味着每个 conversion 操作在 GObject 类型系统下都是一个可独立调度的对象也都可以通过命令行工具vips直接调用如vips embed、vips cast、vips smartcrop。格式类转换改格式、搬波段、换字节序cast任意格式互转与数值裁剪语义Image.cast系列是格式转换的核心入口。原文档列出的便捷方法覆盖 9 种VipsBandFormatcast_uchar、cast_char、cast_ushort、cast_short、cast_uint、cast_int、cast_float、cast_double、cast_complex、cast_dpcomplex它们最终都汇入同一个vips_cast(in, out, format, ...)调用libvips/conversion/cast.c。cast 有三条关键语义均可以从源码确认浮点转整数是截断而非四舍五入。vips_cast的文档注释明确写着 Floats are truncated (not rounded)源码中通过CAST_FLOAT_INT宏直接(double) p[x]赋值给整数目标libvips/conversion/cast.c不经过rint()。需要四舍五入时请先自行 round。越界值会被裁剪clip。所有整型目标的转换都套用了VIPS_CLIP边界宏例如CAST_UCHAR裁剪到[0, UCHAR_MAX]、CAST_SHORT裁剪到[SHRT_MIN, SHRT_MAX]、CAST_UINT裁剪到[0, UINT_MAX]libvips/conversion/cast.c。测试用例 test/test-suite/test_conversion.py 验证了这一点把负数 cast 到无符号格式结果恒为 0把uint最大值 cast 到int结果恒为INT_MAX。复数转实数只取实部。CAST_COMPLEX_INT/CAST_COMPLEX_FLOAT宏在遍历时每次只取p[0]并步进 2 个元素libvips/conversion/cast.c反之实数转复数时虚部置零CAST_REAL_COMPLEX。可选的shift参数默认FALSE布尔型实现整数移位搬移例如 uchar→ushort 时每个值左移 8 位且最低位被复制进新腾出的位因此 255 会变成 65535而不是 65280libvips/conversion/cast.c 的SHIFT_LEFT宏(p[x] n) | (((p[x] 1) n) - (p[x] 1))。这常用于 16 位数据的位深转换避免单纯的移位丢掉低 8 位精度。从 libvips/conversion/cast.c 的 build 逻辑可以看到如果 shift 打开而输入不是整数格式会先 cast 到猜测格式再做最终转换。一个值得注意的实现细节当输入格式与目标格式相同时vips_cast_build会直接退化为vips_image_write即一次纯拷贝不会引入额外计算libvips/conversion/cast.c。另外 cast 使用VIPS_DEMAND_STYLE_THINSTRIP需求风格意味着它适合逐行流水线式处理。copy / copy_file瞬时拷贝与头部字段改写Image.copy是理解 libvips 设计哲学的最好入口VIPS 通过复制指针来复制图像因此这个操作即使对超大图像也是即时的。vips_copy_build中vips_image_pipelinev建立输出图像后vips_copy_gen只是把输入 region 直接 attach 到输出 regionlibvips/conversion/copy.c不产生任何像素计算。copy 的进阶能力是拷贝的同时改写头部字段。可选参数包括width、height、bands、format、coding、interpretation、xres、yres、xoffset、yoffsetlibvips/conversion/copy.c。但有一个硬性约束任何改变单像素字节大小的改动都是非法的——例如可以把 4 波段 uchar 图改成 2 波段 ushort 图两者像素都是 4 字节但不能把 100x100 的 RGB 图直接改成 300x100 的单波段图这会在 build 阶段通过VIPS_IMAGE_SIZEOF_PEL前后对比检查并报错libvips/conversion/copy.c。另外copy 被标记为VIPS_OPERATION_NOCACHElibvips/conversion/copy.c因为它足够便宜且常用于制造独立的图像对象以打破共享不应被操作缓存污染。Image.copy_file是配套的便捷函数若输入本身已落盘则直接 copy 通过否则先用vips_image_new_temp_file(%s.v)建临时文件、写盘后再以文件为源继续处理临时文件在输出图像被关闭时自动删除libvips/conversion/copy.c。这在需要把非文件源如内存 buffer、计算图固化下来以降低内存压力时非常有用。位运算级工具scale、msb、byteswapImage.scale把图像数值线性缩放/平移使数据落入目标格式的满量程范围常与 cast 配合用于显示或归一化。Image.msb取最高有效字节本质是把每个元素右移若干位后取出高字节属于快速降位深的技巧性操作。Image.byteswap交换像素元素的字节序大小端用于处理 endian 不同的数据源。历史上 byteswap 曾是 copy 的swap参数后独立成操作vips_copy中的swap参数如今已被标记为弃用调用时会给出 copy swap is deprecated, use byteswap instead 警告libvips/conversion/copy.c。波段级操作bandjoin、bandfold、bandunfold、bandbool、bandrank、bandmeanImage.bandjoin/bandjoin2/bandjoin_const把多张图像或常量拼接成更多波段的图像。bandjoin_const常用来给图像追加 alpha 或固定值通道测试中colour.bandjoin(1)得到 4 波段图第 4 波段平均值为 1见 test/test-suite/test_conversion.py。Image.bandfold/bandunfold在波段与宽度两种维度布局间互相转换例如把width x height x bands展开为width*bands x height或反之常用于把图像数据当作一维流处理。Image.bandbool/bandand/bandor/bandeor对同像素的各波段做逐位与/或/异或输出单波段图。Image.bandmean对各波段取平均输出单波段图。Image.bandrank对一组图像的同一像素位置按数值排序后取第 n 个值n 阶统计量可实现中值、最大值、最小值滤波等效果。这些操作的共同特征是逐像素处理、不改变空间布局因此它们与 cast 一样走逐行THINSTRIP或逐 tile 的惰性求值路径。波段操作的基础框架集中在VipsBandary基类libvips/conversion/bandary.c、libvips/conversion/bandary.hextract_band也复用该框架。色彩与通道布局recomb、falsecolour、gamma、premultiply 等Image.recomb用一个小矩阵对波段做线性重组线性组合各波段常用于色彩空间矩阵变换本质是逐像素的波段级矩阵乘法。Image.falsecolour把单波段灰度图按查找表映射为彩色伪彩色渲染常用于科学可视化。Image.gamma做幂律伽马校正可选exponent参数。Image.premultiply/unpremultiply在直通straightRGBA与预乘premultipliedRGBA之间转换。预乘是带 alpha 合成composite时常用的中间表示vips_composite默认按非预乘输入处理见下文。像素搬移类转换裁剪、嵌入、旋转与拼接extract_area / crop / smartcrop三种裁剪姿态Image.extract_area从图像中抠出矩形区域参数left, top, width, height均为必填整数。区域必须完整落在输入图像内越界会在 build 阶段直接报 bad extract arealibvips/conversion/extract.c。它的生成器是纯指针搬运——通过vips_region_region把输入 region attach 到输出 region本身不复制像素libvips/conversion/extract.c因此裁剪几乎是零成本的惰性操作。Image.cropextract_area的纯同义词源码中通过g_type_register_static_simple注册了第二个类型函数体直接转发libvips/conversion/extract.c。两者可互换使用。Image.smartcrop只指定目标width, height由算法自动决定保留哪一块。可选interesting参数来自VipsInteresting枚举见后文枚举章节决定显著性的判定算法。源码中有两种实现路径libvips/conversion/smartcrop.cvips_smartcrop_entropy对应VIPS_INTERESTING_ENTROPY迭代地在宽/高方向切下一条兴趣度最低的边每次切片尺寸按目标 8 步收敛自动计算ceil((width - target) / 8.0)兴趣度用vips_hist_findvips_hist_entropy度量libvips/conversion/smartcrop.c对超大图更高效vips_smartcrop_attention对应VIPS_INTERESTING_ATTENTION借鉴 smartcrop.js 思路用肤色向量{-0.78, -0.57, -0.44}等做显著性检测libvips/conversion/smartcrop.c寻找最可能吸引人眼注意力的区域。embed / gravity补边与方向性嵌入Image.embed是extract_area的逆操作把输入图像放进一个更大的画布中位置由x, y指定画布尺寸为width, height。新生成的像素边与角由extend参数决定默认是VIPS_EXTEND_BLACK黑色。源码中 embed 的实现很有代表性libvips/conversion/embed.c输出区域被划分为 8 块上下左右 4 个边 4 个角base-border[8]分别记录这些子矩形libvips/conversion/embed.c。extend为 BLACK/WHITE/BACKGROUND 时用vips_region_paint/vips_region_paint_pel直接填充纯色WHITE 的白由vips_interpretation_max_alpha(in-Type)决定libvips/conversion/embed.c。extend为 COPY 时从输入图像边缘取最近像素平铺vips_embed_base_paint_edge。extend为 REPEAT/MIRROR 时则更巧妙直接组合其他 conversion 操作完成——REPEAT 用vips_replicatevips_extract_area平铺裁剪MIRROR 先vips_flipvips_join拼出 2x2 镜像 tile再 replicate、裁剪、最后用vips_insert把原图覆盖回中心libvips/conversion/embed.c。这充分体现了 conversion 模块内部操作互相复用的设计。一个性能细节当x0, y0且宽高等于输入时embed 直接退化为一次 copylibvips/conversion/embed.c。另外若设置了background而未显式设置extend会自动切到VIPS_EXTEND_BACKGROUNDlibvips/conversion/embed.c。Image.gravity是 embed 的方向版不传 x/y而是传directionVipsCompassDirection让库自动计算位置。9 个方向的位置计算在vips_gravity_build中一目了然CENTRE 为((width-in_width)/2, (height-in_height)/2)EAST 为(width-in_width, (height-in_height)/2)SOUTH_WEST 为(0, height-in_height)等libvips/conversion/embed.c。gravity 与 embed 共用VipsEmbedBase基类因此 extend/background 语义完全一致。flip / rot / rot45 / autorot方向与角度Image.flip沿directionVIPS_DIRECTION_HORIZONTAL左右翻转 /VIPS_DIRECTION_VERTICAL上下翻转镜像图像。源码实现是行/列的反序拷贝水平翻转逐像素倒序 memcpy垂直翻转逐行倒序 memcpylibvips/conversion/flip.c。Image.rot按VipsAngle旋转 90° 的整数倍。rot90、rot180、rot270是它的便捷封装。90°/270° 时输出宽高互换180° 时宽高不变libvips/conversion/rot.c。180° 旋转使用 THINSTRIP 提示90°/270° 因需要转置而改用 SMALLTILE 提示——这是惰性求值下对内存访问模式的权衡。文档还提示任意角度旋转请用Image.similarityresample 模块。Image.rot45按 45° 整数倍旋转对应VipsAngle45枚举D0/D45/.../D315文档特别指出它常用于旋转卷积掩模mask。Image.autorot读取 EXIF orientation 元数据并自动把图像转正同时删除输出图像的 orientation 标签以防二次旋转libvips/conversion/autorot.c。源码中的映射表覆盖全部 8 种 orientationlibvips/conversion/autorot.cEXIF orientation动作1不旋转、不翻转2水平翻转D0 flip3旋转 180°4旋转 180° 翻转5旋转 90° 翻转6旋转 90°7旋转 270° 翻转8旋转 270°实现上 autorot 直接组合vips_rotvips_flipvips_copycopy 是为了安全修改元数据并以输出参数angle、flip报告实际做了哪些操作。配套的Image.autorot_remove_angle则只负责清除元数据包括exif-ifd0-Orientation等 EXIF 字段而不动像素调用前必须先copy一份以免修改共享图像。insert / join / arrayjoin拼接的三种粒度Image.insert把子图sub插入到主图main的(x, y)位置主图尺寸不变超出部分被裁剪。可选expand参数为 TRUE 时输出会扩大以容纳子图background填充新露出的像素。它是 join 的底层基础。Image.join把两张图按direction水平或垂直拼接。参数丰富expand默认 FALSE此时输出高度/宽度取两者较小值为 TRUE 时扩大到容纳全部像素、shim两图间距默认 0、background新像素颜色默认黑色、align对齐方式默认VIPS_ALIGN_LOW低坐标边对齐。源码中 join 是 insert 之上的组合vips_insert(in1, in2, t, x, y, expand, TRUE, ...)后按需vips_extract_area裁剪回非 expand 尺寸libvips/conversion/join.c。波段数不一致时单波段图像会自动复制扩展为多波段再参与运算。Image.arrayjoin一次性把成百上千张图按规则网格拼成大图。文档明确建议如果要在规则网格中拼接成千上万张图arrayjoin 是比反复 join 更好的选择——因为 arrayjoin 直接建立整个输出的几何关系避免串联 join 带来的中间缓存开销。replicate / grid / wrap / transpose3d / zoom / subsampleImage.replicate把图像在横纵方向复制指定次数输出尺寸为in.Xsize * across x in.Ysize * down。Image.grid把单张图按across列切割成若干子块并重排拼接为网格可用于生成缩略图拼贴。Image.wrap把图像像卷轴一样卷绕平移像素从一侧溢出绕回到另一侧可指定x、y位移。Image.transpose3d对三维数据把width x height x bands视为width x (height*bands)做转置本质上交换高度与波段两个轴的布局。Image.zoom最近邻整数倍放大xfac、yfac指定倍数输出像素直接复制。Image.subsample整数倍缩小按xfac、yfac间隔抽取像素。源码注释中特别提到该操作曾移除 SEQUENTIAL 提示以配合vips_sequential()使用libvips/conversion/subsample.c。缓存与访问模式tilecache、linecache、sequential这一组操作不改变像素内容而是改变求值时的缓存与访问策略是 libvips 惰性求值demand-driven架构下控制内存/IO 的关键工具Image.tilecache按矩形 tile 缓存上游计算结果配合tile_width、tile_height、max_tiles等参数实现随机访问大图而不重复计算。适合需要多次不同位置裁剪同一源图的场景。Image.linecache按水平条带缓存tile_height控制条带高度内存占用比 tilecache 更省但只适合逐条带顺序访问。Image.sequential强制检查像素只按自上而下顺序请求。vips_sequential_build内部通过vips_linecache(..., access, VIPS_ACCESS_SEQUENTIAL, ...)实现libvips/conversion/sequential.ctile_height默认 1。它专门服务于只支持自上而下解码的格式如 PNG当你在 PNG 源后接 crop/rot 等需要随机访问的操作时sequential会保证上游按序解码从而把内存占用压到最低。源码注释也给出了使用建议如果想从同一源做多次 crop应改用 RANDOM 访问模式即 tilecache因为持久化 sequential 缓存内存开销较大libvips/conversion/sequential.c。合成与条件选择composite、ifthenelse、switchcomposite / composite2 与 28 种 BlendModeImage.composite把n张图按图层栈合成in[0]在最底in[n-1]在最上自下而上逐层用mode数组中对应的混合模式混合libvips/conversion/conversion.c。关键语义包括自动转换到合成空间默认在 sRGB、B_W、RGB16 或 GREY16 中选一个取决于输入波段数与位深也可用compositing_space指定其他空间如 LAB、scRGB。输出格式总是 FLOAT除非某个输入是 DOUBLE此时输出也为 DOUBLE复数图像不支持。强制 alpha输出必定带 alpha 波段缺少 alpha 的输入会自动补一个实心 alpha。尺寸与位置输入无需同尺寸同格式输出尺寸恒等于in[0]其余图像通过x、y数组长度 n-1定位超出in[0]范围的部分被裁剪。预乘处理默认按非预乘straight输入处理可直接用于 PNG若输入经过premultiply须设置premultipliedTRUE。Image.composite2是两图版便捷封装composite2(base, overlay, mode, x, y, ...)。混合模式来自VipsBlendMode枚举源码文档给出了 28 个成员的精确语义libvips/conversion/conversion.cPorter-Duff 系列CLEAR第二物体处移除第一物体、SOURCE、OVER两张半透明幻灯片叠加效果、IN、OUT、ATOP、DEST及各自的 DEST_ 镜像版本、XOR以及 PDF 混合系列ADD、SATURATE、MULTIPLY、SCREEN、OVERLAY、DARKEN、LIGHTEN、COLOUR_DODGE、COLOUR_BURN、HARD_LIGHT、SOFT_LIGHT、DIFFERENCE、EXCLUSION、HUE、SATURATION、COLOUR、LUMINOSITY。ifthenelse / switchImage.ifthenelse三目选择——cond为真处取in1像素否则取in2像素可配合blend参数做软过渡是条件式图像处理的基础构件。Image.switch多分支选择接受一个条件图像数组和多个候选图像按第一个为真的条件挑选像素。枚举速查8 个关键枚举conversion 模块大量使用 GObject 枚举作为参数这里汇总其成员与用途来源libvips/conversion/conversion.c 中的 gtk-doc 注释枚举用途成员VipsExtendembed/conv/affine 等的边界扩展方式BLACK全 0 黑、COPY复制边缘像素、REPEAT平铺整图、MIRROR镜像平铺以减少接缝、WHITE全 1 白、BACKGROUND用background属性颜色VipsCompassDirectiongravity 的 9 向定位CENTRE、NORTH、EAST、SOUTH、WEST、NORTH_EAST、SOUTH_EAST、SOUTH_WEST、NORTH_WESTVipsDirectionflip/join 的方向HORIZONTAL左右、VERTICAL上下VipsAlignjoin 等操作的对齐边LOW低坐标边、CENTRE居中、HIGH高坐标边VipsAnglerot 的固定角度D0、D90顺时针、D180、D270逆时针 90°VipsAngle45rot45 的 45° 倍数D0、D45、D90、D135、D180、D225、D270、D315VipsInterestingsmartcrop 的显著性算法NONE等同于 LOW取顶部/左侧、CENTRE居中、ENTROPY熵度量、ATTENTION人眼注意力、LOW、HIGH底部/右侧、ALL、SPECIFIC指定兴趣点VipsBlendModecomposite 混合模式28 个成员见上文值得一提的兼容性细节源码注释明确说明VipsExtend等枚举成员值必须保持冻结we have to keep these frozen for back compat with vips7这也是这些枚举编号不能随意调整的原因libvips/conversion/conversion.c。测试与验证仓库提供了系统性的回归测试 test/test-suite/test_conversion.py约 990 行覆盖本模块绝大多数操作可作为理解参数语义的活文档cast 的裁剪语义负值→无符号格式裁剪为 0uint/ushort/uchar 最大值→有符号格式裁剪为对应最大值test/test-suite/test_conversion.py。波段逻辑运算bandand/bandor/bandeor与 Python 内置位运算逐像素对比test/test-suite/test_conversion.py。bandjoinx.bandjoin(y)与x y结果一致性验证test/test-suite/test_conversion.py。bandjoin_const追加常值波段后bands数、像素值断言test/test-suite/test_conversion.py。测试基类run_unary/run_binary会对每种VipsBandFormat组合执行同一函数并与参考实现对比这种跨格式一致性测试正是 conversion 操作强健性的保证。实践要点小结裁剪用crop/extract_area零拷贝、惰性智能裁剪用smartcropinteresting补边用embed精确坐标或gravity方向定位边界模式在extend中选。换格式用cast系列注意截断而非四舍五入、越界裁剪两条语义位深搬移用shift参数改头部字段用copy但不可改变像素字节大小。拼接两图用join可对齐、加间距规则大网格用arrayjoin混合叠加用composite/composite2BlendMode。控制内存PNG 等顺序解码源后接随机访问操作时用sequential多次随机裁剪同一源用tilecache。转正方向根据 EXIF 自动转正用autorot它会在旋转/翻转后清除 orientation 标签防止二次旋转。所有上述操作都可在 libvips/conversion 目录下找到对应源文件也可通过vips命令行工具直接调用如vips embed in.v out.v 10 10 200 200 --extend black测试基准参考 test/test-suite/test_conversion.py。【免费下载链接】libvipsA fast image processing library with low memory needs.项目地址: https://gitcode.com/gh_mirrors/li/libvips创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
