guetzli图片压缩php扩展

2024-05-07 18:38
文章标签 php 图片 扩展 压缩 guetzli

本文主要是介绍guetzli图片压缩php扩展,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

前言:在github的发现一个谷歌开源的jpeg格式图片的压缩工具,它可以在主观质量无损的条件下, 将jpeg图片减少20%~30%的码率,于是当时测试了一下压缩效果,图片质量95压缩率大概在20%~30%。我们司平台是漫画阅读app,整个app除了图片还是图片,特别是首页,用了大量的高清无码的图片,于是我想将guetzli用于首页漫画封面图片的压缩,但是guetzli并没有提供php的接口,所以当时的实现是将需要压缩的图片放入redis队列,创建一个进程在凌晨基本没有用户访问的时候通过exec调用guetzli工具对redis队列消费处理。(至于为什么要这样,请看下面网上大佬的测试报告)


以下数据引用自这篇博客:谷歌开源图片压缩算法Guetzli实测体验报告

Guetzli资源消耗:

官方文档说,1MPIX的图片处理需要消耗300M内存。

实测一个1MB大小1920x2560的图片,有4.9MPIX。理论消耗内存1474MB,实际消耗1009MB内存,实际与理论基本相符。由此看出这个工具是个内存消耗大户,60G内存只够处理200MPIX,也就是同时处理40张左右的1920x2560图片。

CPU消耗则一直是100%单核占用。当然实际使用多核机器可以同时跑多个进程。有多少核就能跑多少个guetzli任务。

压缩图片耗时跟图片大小程离散相关,图片越大,耗时越久

guetzli劣势:

由于guetzli时效性差,图片越大压缩时间越长,而且资源占用大,简直就是内存消耗大户。

guetzli优势:

实测对质量在90或以下的jpg图片,guetzli输出的新图质量不会降低。而实际压缩率能够达到平均压缩率29%。

兼容性比较好,输出的jpeg格式图片通用性非常高。没有webp、sharpp那种协议不兼容的困扰。

在客户端jpeg格式的图片编解码速度比其他私有协议快很多。


下面说说我们将要做的:

从github下载guetzli的源码编译后会生成一个guetzli的工具,以及一个静态库文件,我们需要做的就是创建一个php扩展调用这个静态库提供的压缩功能。

动态加载so模块:利用ext_skel工具编译生成so模块,修改php.ini文件,动态加载即可
静态编译:将编写的模块静态编译到PHP,需要重新编译PHP

面临的问题:

注:php扩展加载方式可以分为静态编译与动态加载。(区别在于,静态编译需要重新编译php才能使用扩展,动态加载是将php扩展编译成so动态库库,修改php.ini,php在运行时会动态加载,不需要重新编译php),我们这里需要将扩展编译成so动态库。

1、php动态so库需要依赖guetzli编译生成的静态库,guetzli的静态库在编译的时候生成的目标文件是地址相关的,而动态库的一个特点是动态库是被进程动态加载到一个不确定的地址,所以动态库编译时的目标文件为地址无关的相对路径,而没有绝对路径。在这里我们的php动态库需要引用guetzli的静态库,所以我们需要修改guetzli的Makefile文件;

2、guetzli源码使用c++编写的,但是我们的php扩展接口是用c进行编写,由于c函数跟c++函数在编译的时候对参数的处理不一样,所以这里需要导出一个c接口,供php扩展调用;

创建php扩展模板:

注:本文章不详细讨论php扩展开发基础知识,后面会详细写一篇php扩展方面的文章;

1、用php源码扩展目录ext下的ext_skel工具创建php扩展的模板,在ext目录下运行./ext_skel --extname=myguetzli命令(myguetzli是我们需要创建扩展的名称),这个工具会在etx目录下创建一个myguetzli目录,里面包含我们扩展开发所必需的基本文件。


2、我们需要使用到的是config.m4、myguetzli.c、myguetzli.h这三个文件


3、我们在myguetzli目录下创建一个guetzli目录用于存放guetzli的静态库以及导出的c函数接口文件


php扩展模板有了,我们接下来解决上面提到问题:

第一步:修改guetzli源码目录的Makefile文件,让guetzli在make编译的时候生成的目标代码是地址不相关的。(生成地址不相关的目标文件,需要在编译的时候加上-fPIC编译选项

1、guetzli源码目录下有一个guetzli_static.make,这个是用于编译guetzli静态库的Makefile文件;

2、在这里文件里找到下面这行,并加上红色部分的选项,保存;

3、运行make进行编译即可。会在bin/Release目录下生成一个guetzli_static.a的静态库文件;到这里第一个问题就解决了。

//4、将guetzli_static.a静态库拷贝到php扩展myguetzli目录下的guetzli/lib目录,guetzli源码目录的guetzli目录里面的头文件也拷贝到myguetzli目录下的guetzli目录

ALL_CXXFLAGS += $(CXXFLAGS) $(ALL_CPPFLAGS) -O3 -g -std=c++11 -fPIC `pkg-config --static --cflags libpng || libpng-config --static --cflags`
第二步:创建接口文件
1、guetzli源码目录下有一个guetzli.cc文件,这个是guetzli提供的工具源代码,写好了对guetzli压缩功能的调用,
我们在这个文件里面增加一个函数,并用c函数的方式编译这个函数进入扩展myguetzli下的guetzli目录,创建两个文件(guetzli.cpp、guetzli.h)
新建一个guetzli.h文件,extern告诉编译器按c函数的方式编译,这样才能被c所调用
#guetzli.h文件
#ifdef __cplusplus
extern "C"
{
#endif
int MyGuetzli(char* filename, char* savefilename, int quality, int memlimit_mb);
#ifdef __cplusplus
}
#endif
在guetzli.cc文件新增MyGuetzli函数
#guetzli.cpp文件,将guetzli源码目录guetzli.cc文件的代码复制过来,并增加下面的代码
int MyGuetzli(char* filename, char* savefilename, int quality, int memlimit_mb)
{int verbose = 0;// int quality = kDefaultJPEGQuality;memlimit_mb = kDefaultMemlimitMB;std::string in_data = ReadFileOrDie(filename);std::string out_data;guetzli::Params params;params.butteraugli_target = static_cast<float>(guetzli::ButteraugliScoreForQuality(quality));guetzli::ProcessStats stats;if (verbose) {stats.debug_output_file = stderr;}static const unsigned char kPNGMagicBytes[] = {0x89, 'P', 'N', 'G', '\r', '\n', 0x1a, '\n',};if (in_data.size() >= 8 &&memcmp(in_data.data(), kPNGMagicBytes, sizeof(kPNGMagicBytes)) == 0) {int xsize, ysize;std::vector<uint8_t> rgb;if (!ReadPNG(in_data, &xsize, &ysize, &rgb)) {fprintf(stderr, "Error reading PNG data from input file\n");return 1;}double pixels = static_cast<double>(xsize) * ysize;if (memlimit_mb != -1&& (pixels * kBytesPerPixel / (1 << 20) > memlimit_mb|| memlimit_mb < kLowestMemusageMB)) {fprintf(stderr, "Memory limit would be exceeded. Failing.\n");return 1;}if (!guetzli::Process(params, &stats, rgb, xsize, ysize, &out_data)) {fprintf(stderr, "Guetzli processing failed\n");return 1;}} else {guetzli::JPEGData jpg_header;if (!guetzli::ReadJpeg(in_data, guetzli::JPEG_READ_HEADER, &jpg_header)) {fprintf(stderr, "Error reading JPG data from input file\n");return 1;}double pixels = static_cast<double>(jpg_header.width) * jpg_header.height;if (memlimit_mb != -1&& (pixels * kBytesPerPixel / (1 << 20) > memlimit_mb|| memlimit_mb < kLowestMemusageMB)) {fprintf(stderr, "Memory limit would be exceeded. Failing.\n");return 1;}if (!guetzli::Process(params, &stats, in_data, &out_data)) {fprintf(stderr, "Guetzli processing failed\n");return 1;}}WriteFileOrDie(savefilename, out_data);return 0;
}

2、在myguetzli扩展下的guetzli目录执行如下代码

g++ guetzli.cpp -std=c++11 -fPIC -shared -I/home/liaokw/下载/guetzli -L/home/liaokw/下载/guetzli/bin/Release -lguetzli_static  -o ./lib/libguetzli.so


-I:指定guetzli的头文件
-L:指定guetzli静态库
-l:guetzli静态库的名称
-o:生成动态库保存路径



libguetzli.so这个动态库就是我们扩展所要要使用的,这个动态库主要作为调用guetzli的接口



第三步:php扩展开发
1、修改config.m4,编写一些宏函数,用于检测以及添加扩展所依赖的库,以及环境(扩展依赖于第二部编译的动态库,guetzli依赖于png库)
在config.m4文件添加以下代码
PHP_ARG_WITH(png-dir, for myguetzli support,
Make sure that the comment is aligned:
[  --with-png-dir             Include myguetzli support])if test "$PHP_PNG_DIR" != "no"; thenSEARCH_PATH="$PHP_PNG_DIR /usr/lib /usr/local/lib"     # you might want to change thisSEARCH_FOR="include/png.h"  # you most likely want to change thisfor i in $SEARCH_PATH ; doif test -f $i/$SEARCH_FOR; thenPNG_DIR=$iAC_MSG_RESULT(found in $i)fidoneif test -z "$PNG_DIR" -o -z "$PNG_DIR/include"; thenAC_MSG_RESULT([$PNG_DIR not found 1])AC_MSG_ERROR([$SEARCH_PATH || $PNG_DIR ||  Please reinstall the png distribution])fidnl 头文件路径PHP_ADD_INCLUDE($PNG_DIR/include)PHP_CHECK_LIBRARY(png,png_create_read_struct,[PHP_ADD_LIBRARY_WITH_PATH(png, $PNG_DIR/lib, MYGUETZLI_SHARED_LIBADD)AC_DEFINE(HAVE_PNGLIB,1,[libpng yes ])],[AC_MSG_ERROR([$PNG_DIR/include no found 2])],[-L$PNG_DIR/lib -lpng])PHP_SUBST(MYGUETZLI_SHARED_LIBADD)
fi上面的代码主要用于在configure添加一个配置参数--with-png-dir,用于指定png库所在路径if test "$PHP_MYGUETZLI_DIR" != "no"; thenSEARCH_PATH="$PHP_MYGUETZLI_DIR guetzli"     # you might want to change thisSEARCH_FOR="/guetzli.h"  # you most likely want to change thisfor i in $SEARCH_PATH ; doif test -f $i/$SEARCH_FOR; thenGUETZLI_DIR=$iAC_MSG_RESULT(found in $i)fidoneif test -z "$GUETZLI_DIR" -o -z "$GUETZLI_DIR/"; thenAC_MSG_RESULT([$GUETZLI_DIR not found 1])AC_MSG_ERROR([$SEARCH_PATH || $GUETZLI_DIR ||  Please reinstall the zhtmltopdf distribution])fidnl 头文件路径PHP_ADD_INCLUDE($GUETZLI_DIR)PHP_ADD_LIBRARY_WITH_PATH(guetzli, $GUETZLI_DIR/lib, MYGUETZLI_SHARED_LIBADD)AC_DEFINE(HAVE_MYGUETZLILIB,1,[libguetzli yes ])dnl  PHP_CHECK_LIBRARY(guetzli,MyGuetzli,
dnl  [
dnl    PHP_ADD_LIBRARY_WITH_PATH(guetzli, $GUETZLI_DIR/lib, MYGUETZLI_SHARED_LIBADD)
dnl    AC_DEFINE(HAVE_MYGUETZLILIB,1,[libguetzli yes ])
dnl  ],[
dnl   AC_MSG_ERROR([$GUETZLI_DIR/include no found 2])
dnl  ],[
dnl   -L$GUETZLI_DIR/lib -lguetzli
dnl  ])PHP_SUBST(MYGUETZLI_SHARED_LIBADD)
fi

上面的宏代码用于检测以及添加我们在第二步生成的动态库
通过执行phpize程序,会将上面一系列宏函数替换成检测编译环境的shell代码,并配置我们所需要的依赖库

2、编辑myguetzli.c扩展文件,需要实现一个myguetzlijpg函数给用户层调用,修改两处地方以及引用guetzli.h文件,用于使用接口库导出的函数


3、编译扩展并安装即可使用了

哎,第一次写文章,逻辑处理的不够好,写的也一塌糊涂,漏洞百出;很多东西自己知道但是不知道该如何表达出来;


这篇关于guetzli图片压缩php扩展的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/968070

相关文章

基于Python实现一个图片拆分工具

《基于Python实现一个图片拆分工具》这篇文章主要为大家详细介绍了如何基于Python实现一个图片拆分工具,可以根据需要的行数和列数进行拆分,感兴趣的小伙伴可以跟随小编一起学习一下... 简单介绍先自己选择输入的图片,默认是输出到项目文件夹中,可以自己选择其他的文件夹,选择需要拆分的行数和列数,可以通过

PostgreSQL的扩展dict_int应用案例解析

《PostgreSQL的扩展dict_int应用案例解析》dict_int扩展为PostgreSQL提供了专业的整数文本处理能力,特别适合需要精确处理数字内容的搜索场景,本文给大家介绍PostgreS... 目录PostgreSQL的扩展dict_int一、扩展概述二、核心功能三、安装与启用四、字典配置方法

Linux中压缩、网络传输与系统监控工具的使用完整指南

《Linux中压缩、网络传输与系统监控工具的使用完整指南》在Linux系统管理中,压缩与传输工具是数据备份和远程协作的桥梁,而系统监控工具则是保障服务器稳定运行的眼睛,下面小编就来和大家详细介绍一下它... 目录引言一、压缩与解压:数据存储与传输的优化核心1. zip/unzip:通用压缩格式的便捷操作2.

利用Python脚本实现批量将图片转换为WebP格式

《利用Python脚本实现批量将图片转换为WebP格式》Python语言的简洁语法和库支持使其成为图像处理的理想选择,本文将介绍如何利用Python实现批量将图片转换为WebP格式的脚本,WebP作为... 目录简介1. python在图像处理中的应用2. WebP格式的原理和优势2.1 WebP格式与传统

基于 HTML5 Canvas 实现图片旋转与下载功能(完整代码展示)

《基于HTML5Canvas实现图片旋转与下载功能(完整代码展示)》本文将深入剖析一段基于HTML5Canvas的代码,该代码实现了图片的旋转(90度和180度)以及旋转后图片的下载... 目录一、引言二、html 结构分析三、css 样式分析四、JavaScript 功能实现一、引言在 Web 开发中,

Python如何去除图片干扰代码示例

《Python如何去除图片干扰代码示例》图片降噪是一个广泛应用于图像处理的技术,可以提高图像质量和相关应用的效果,:本文主要介绍Python如何去除图片干扰的相关资料,文中通过代码介绍的非常详细,... 目录一、噪声去除1. 高斯噪声(像素值正态分布扰动)2. 椒盐噪声(随机黑白像素点)3. 复杂噪声(如伪

Python中图片与PDF识别文本(OCR)的全面指南

《Python中图片与PDF识别文本(OCR)的全面指南》在数据爆炸时代,80%的企业数据以非结构化形式存在,其中PDF和图像是最主要的载体,本文将深入探索Python中OCR技术如何将这些数字纸张转... 目录一、OCR技术核心原理二、python图像识别四大工具库1. Pytesseract - 经典O

Python实现精准提取 PDF中的文本,表格与图片

《Python实现精准提取PDF中的文本,表格与图片》在实际的系统开发中,处理PDF文件不仅限于读取整页文本,还有提取文档中的表格数据,图片或特定区域的内容,下面我们来看看如何使用Python实... 目录安装 python 库提取 PDF 文本内容:获取整页文本与指定区域内容获取页面上的所有文本内容获取

Python基于微信OCR引擎实现高效图片文字识别

《Python基于微信OCR引擎实现高效图片文字识别》这篇文章主要为大家详细介绍了一款基于微信OCR引擎的图片文字识别桌面应用开发全过程,可以实现从图片拖拽识别到文字提取,感兴趣的小伙伴可以跟随小编一... 目录一、项目概述1.1 开发背景1.2 技术选型1.3 核心优势二、功能详解2.1 核心功能模块2.

Go语言如何判断两张图片的相似度

《Go语言如何判断两张图片的相似度》这篇文章主要为大家详细介绍了Go语言如何中实现判断两张图片的相似度的两种方法,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 在介绍技术细节前,我们先来看看图片对比在哪些场景下可以用得到:图片去重:自动删除重复图片,为存储空间"瘦身"。想象你是一个