php 如何辨别网页图片是静态的还是动态的?

bvhaajcl  于 2023-09-29  发布在  PHP
关注(0)|答案(8)|浏览(135)

我正在做一个项目,用户可以上传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。

uinbv5nw

uinbv5nw1#

在Webp头中有标志,其中包括动画。小函数来检查它:

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来自下一个块头。
RIFF container specification

yrdbyhpb

yrdbyhpb2#

经过大量的调查,我发现动画webp图像总是包含一些字符串,当在文本编辑器中打开时,非动画图像不会。字符串为ANMFANIM。我检查了这些字符串在所有的webp图像,我有。所以这对我来说太完美了。以下是PHPJavascriptShell 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 isWebpAnimated(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();
}

但在大图像PHPJavascript的情况下不能很好地工作,所以最好的解决方案是使用Shell Script,如果你有Ubuntu .
在Shell脚本中:

echo $(grep -c "ANMF" ~/animated.webp)

返回0如果非动画,否则非零值为动画。
编辑:Python

def isWebpAnimated(src):
    try:
        with open(src, 'rb') as f:
            data = f.read()

        # Check if the file starts with 'RIFF' and contains 'WEBPVP8' or 'WEBPVP8X'
        if data.startswith(b'RIFF') and (b'WEBPVP8' in data or b'WEBPVP8X' in data):
            # The 'ANIM' indicating that the given file is Animated
            return 1
        else:
            return 0

    except Exception as err:
        exit(f"Error Occure: {err}")
eoigrqb6

eoigrqb63#

根据Sven LiivakisWebpAnimated() ...有一个小bug。
fseek($fh, 16);
应该是:
fseek($fh, 20);
因为位置16VP8X中的chunk_size位置。但是我们需要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;
}
qcuzuvrc

qcuzuvrc4#

这是我的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;
    }
f0brbegy

f0brbegy5#

修复@ccomangee的解决方案一些静态webp图像将被检测为动画,并可能导致应用程序中的问题。
我提取了webp帧并保存为webp图像,并试图通过检查VP 8X签名来识别,它存在于给定的位置,尽管它是静态图像。因此,如果有VP 8X,这并不意味着图像将具有多个帧的动画。
我尝试了几个图像与我的解决方案,结果如下:

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:不透明的动画图像、静态变换和静态立体都是静态图像。
VP 8L是无损的webp,VP 8X包含扩展功能。VP 8当然是静态图像。
如果VP 8X存在,它可以是静态的,也可以是动画的,大多数图像都是动画的。
将溶液
读取4个字节-> 'RIFF'跳过4个字节读取4个字节-> 'WEBP'读取4个字节-> 'VP 8X' / 'VP 8L' / 'VP 8'跳过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

wd2eg0qa

wd2eg0qa6#

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
vwkv1x7d

vwkv1x7d7#

这是我在BASH(Linux)中的解决方案。在Debian 12中工作,无需添加任何必要的软件。搜索和复制动画webp文件到桌面。这是很容易修改我的脚本,以适应您的需要。一些想法来自这里。

#!/bin/bash
file=$(zenity  --file-selection --filename=$HOME/ --title="Choose a directory to convert all file" --directory)
rm "/dev/shm/findaniwebp.txt" 2> /dev/null
rm "/dev/shm/findfiles.txt" 2> /dev/null
find "$file" -iname '*.webp' >> "/dev/shm/findfiles.txt" 2>/dev/null

{
result=0
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
fnx2tebb

fnx2tebb8#

我很抱歉很晚才加入这个讨论,但我也在寻找一个基于Python的离线使用解决方案。
虽然我不精通PHP或JavaScript,但我对Python有一些了解。这是我的发现:您可以使用Python Imaging Library(PIL)确定WebP图像是动画还是静态图像。
要计算WebP图像中的帧数并识别动画,您可以使用PIL的ImageImageSequence模块。下面是一个Python脚本来实现这一点:

from PIL import Image, ImageSequence

def is_animation(file):
    image = Image.open(file)
    count = 0
    for frame in ImageSequence.Iterator(image):
        count += 1
    return count > 1

通过使用此代码,可以调用is_animation函数,并将文件路径作为参数传递。如果图像包含多个帧,函数将返回True,表示它是一个动画;否则,它将返回静态图像的False

相关问题