我正在做一个用户可以上传
webp
图片的项目。我知道如何将 webp
图像转换为 jpg/png,但我仍然不知道如何识别 webp
图像是静态(非动画)还是动画。
我想识别它们,因为我使用不同的命令进行转换:
非动画webp转jpg的命令:
dwebp nonanimated.webp -o jpg.jpg
动画 webp 到非动画 webp 的命令(拍摄第二帧):
webpmux -get frame 2 animated.webp -o nonanimated.webp
但是我找不到一个命令来处理这两种情况。
我在服务器端使用 PHP,前端使用 HTML 和 Javascript。
经过大量调查,我发现动画
webp
图像总是包含一些字符串,当在文本编辑器中打开时,非动画图像则不会。字符串是ANMF
和ANIM
。我在我拥有的所有 webp 图像中检查了这些字符串。所以这对我来说是完美的。以下是 PHP
、Javascript
和 Shell Script
中的一些解决方案:
在 PHP 中:
<?php
function isWebpAnimated($src){
$webpContents = file_get_contents($src);
$where = strpos($webpContents, "ANMF");
if ($where !== FALSE){
// animated
$isAnimated = true;
}
else{
// non animated
$isAnimated = false;
}
return $isAnimated;
}
?>
在Javascript中:
function isAnimatedGif(src) {
var request = new XMLHttpRequest();
request.open('GET', src, true);
request.addEventListener('load', function () {
if(request.response.indexOf("ANMF") != -1){
// animated
alert(true);
}
else{
// non animated
alert(false);
}
});
request.send();
}
但是如果大图像
PHP
和Javascript
效果不佳,那么最好的解决方案是使用Shell Script
,如果你有Ubuntu
.
在 Shell 脚本中:
echo $(grep -c "ANMF" ~/animated.webp)
如果非动画则返回 0,否则为动画返回非零值。
Webp 标头中有标志,ANIMATION 等。检查它的小功能:
function isWebpAnimated($fn){
$result = false;
$fh = fopen($fn, "rb");
fseek($fh, 12);
if(fread($fh, 4) === 'VP8X'){
fseek($fh, 16);
$myByte = fread($fh, 1);
$result = ((ord($myByte) >> 1) & 1)?true:false;
}
fclose($fh);
return $result;
}
ANIM 和 ANMF 来自下一个块头。
根据Sven Liivak的
isWebpAnimated()
...有一个小错误。
fseek($fh, 16);
应该是:
fseek($fh, 20);
因为位置
16
是chunk_size
中的VP8X
位置。
但是我们需要位于flag
的20
位置。
固定功能:
function isWebpAnimated($fn){
$result = false;
$fh = fopen($fn, "rb");
fseek($fh, 12);
if(fread($fh, 4) === 'VP8X'){
fseek($fh, 20);
$myByte = fread($fh, 1);
$result = ((ord($myByte) >> 1) & 1)?true:false;
}
fclose($fh);
return $result;
}
这是我的 java 代码,对我有用。
static boolean isWebpAnimated(InputStream in) {
boolean result = false;
try {
in.skip(12);
byte[] buf = new byte[4];
int i = in.read(buf);
if ("VP8X".equals(new String(buf, 0, i))) {
in.skip(12);
result = (in.read(buf) == 4 && (buf[3] & 0x00000002) != 0);
}
} catch (Exception e) {
} finally {
try {
in.close();
} catch (Exception e) {
}
}
return result;
}
修复@ccomangee 的解决方案。 一些静态 webp 图像会被检测为动画,并可能导致应用程序出现问题。
我提取了 webp 帧并保存为 webp 图像,并尝试通过检查 VP8X 签名来识别,它存在于给定位置,尽管它是静态图像。因此,如果有 VP8X,这并不意味着图像将具有多于一帧的动画效果。
我用我的解决方案尝试了几张图片,结果如下:
riff webp vp8* anim
OK(anim-trans).webp: [ RIFF | WEBP | VP8X | ANIM ]
Cuppy(static-trans).webp: [ RIFF | WEBP | VP8L | NA? ]
glass(anim-solid).webp: [ RIFF | WEBP | VP8X | ANIM ]
sunset(anim-trans).webp: [ RIFF | WEBP | VP8X | ANIM ]
atom(anim_solid).webp: [ RIFF | WEBP | VP8X | ANIM ]
spread(anim-trans).webp: [ RIFF | WEBP | VP8X | ANIM ]
heart(static-trans).webp: [ RIFF | WEBP | VP8X | NA? ]
ludo(static-trans).webp: [ RIFF | WEBP | VP8X | NA? ]
scene(static_solid).webp: [ RIFF | WEBP | VP8 | NA? ]
这里所有的图片都根据其类型命名。 anim-trans:动画图像包含透明度(alpha 通道支持) anim-solid:没有透明度的动画图像 static-trans 和 static-solid 是静态图像。
VP8L 是无损 webp,VP8X 包含扩展功能。 VP8肯定是静态图像。
如果 VP8X 存在,它可以是静态的或动画的,大多数图像将是动画的。
解决方案是
读取 4 个字节 -> 'RIFF' 跳过 4 个字节 读取 4 个字节 -> 'WEBP' 读取 4 个字节 -> 'VP8X' / 'VP8L' / 'VP8' 跳过 14 个字节 读取 4 个字节 -> 'ANIM'
Java代码:
public static boolean check(File file) {
boolean riff = false;
boolean webp = false;
boolean vp8x = false;
boolean anim = false;
try (InputStream in = new FileInputStream(file)) {
byte[] buf = new byte[4];
int i = in.read(buf); // 4
if(buf[0] == 0x52 && buf[1] == 0x49 && buf[2]==0x46 && buf[3] == buf[2] )
riff = true;
in.skip(4); // ???? (8+)
i = in.read(buf); // (12+)
if(buf[0] == 0x57 && buf[1] == 0x45 && buf[2]==0x42 && buf[3] == 0x50 )
webp = true ;
i = in.read(buf); // (16+)
if(buf[0] == 0x41 && buf[1] == 0x4e && buf[2]==0x49 && buf[3] == 0x4d );
vp8x = true;
in.skip(14); // then next 4 should contain ANIM - 41 4e 49 4d
i = in.read(buf);
if(buf[0] == 0x41 && buf[1] == 0x4e && buf[2]==0x49 && buf[3] == 0x4d )
anim = true;
} catch (Exception e) {
System.out.println("errrrrr "+e.getMessage());
}
return riff && webp && anim;
}
您可以通过跳过 8 个字节直接读取 WEBP,然后计算并跳过 ANIM 之前的所有块,如果 ANIM 存在则读取该位置然后其动画 webp 图像否则为静态。
webp图片的文件布局 https://developers.google.com/speed/webp/docs/riff_container#example_file_layouts
参考:Google WEBP 规范https://developers.google.com/speed/webp/docs/riff_container
def is_webp_animation(img_content):
# https://developers.google.com/speed/webp/docs/riff_container#extended_file_format
# webp Animation image.
return len(img_content) > 20 and img_content[0:4] == b'RIFF' and img_content[8:12] == b'WEBP' \
and img_content[12:16] == b'VP8X' \
and int.from_bytes(img_content[20:21], 'little') & 2 == 2
这是我在 BASH (Linux) 中的解决方案。在 Debian 12 中工作,无需添加任何所需的软件。搜索动画 webp 文件并将其复制到桌面。很容易修改我的脚本以满足您的需要。一些想法来自这里。
#!/bin/bash
file=$(zenity --file-selection --filename=$HOME/ --title="Choose a directory to convert all file" --directory) ## Directory select.
result=0
rm "/dev/shm/findaniwebp.txt" 2> /dev/null
rm "/dev/shm/findfiles.txt" 2> /dev/null
find "$file" -name lost+found -prune -o -name '*.webp' >> "/dev/shm/findfiles.txt"
{
input="/dev/shm/findfiles.txt"
while IFS= read -r "line"
do
result=$(echo $(grep -c "ANMF" "$line"))
if [ "$result" -ge 10 ]; then
echo "Animated webp is found !"
echo $line
echo $line >> "/dev/shm/findaniwebp.txt"
fi
done < "$input"
}
if [ ! -f "/dev/shm/findaniwebp.txt" ]
then
aniwebp=0
else
aniwebp=$(wc -l < "/dev/shm/findaniwebp.txt")
echo "Finding finish (webp only) , with file to move : $aniwebp"
fi
if [ "$aniwebp" -ge "1" ]; then
if zenity --no-wrap --question --text="Do you want to COPY theseS fileS to $HOME/Desktop ?"
then
{
input="/dev/shm/findaniwebp.txt"
while IFS= read -r "line"
do
cp "$line" "$HOME"/Desktop
echo file moved...
done < "$input"
}
fi
else
echo "NO animated webp found!"
fi
rm "/dev/shm/findaniwebp.txt" 2> /dev/null
rm "/dev/shm/findfiles.txt" 2> /dev/null
read -n 1 -s -r -p "Press ENTER key to exit !"
exit