在Flutter中,如何从gif动画中逐帧提取?

问题描述 投票:0回答:2

我正在尝试使用 Flutter 从动画 gif 中提取帧。通过研究 Flutter API,我认为以下代码应该可以工作,但它只给了我第一帧,尽管它给了我正确的帧计数。

static Future<List<ui.Image>> loadAnimatedGif(String assetPath) async {
    List<ui.Image> gifImage = [];
    ByteData data = await rootBundle.load(assetPath);
    var codec = await ui.instantiateImageCodec(data.buffer.asUint8List());
    int frmCnt = codec.frameCount;
    print("frmcnt is $frmCnt"); //it gives correct frames count
    for (int i = 0; i < frmCnt; i++) {
      var frame = await codec.getNextFrame();
      gifImage.add(frame.Image);
     
    }
   
    return gifImage;
  }
flutter image extract frame animated-gif
2个回答
2
投票

我用这个制作了一个简单的应用程序,它使用相同的代码工作,但在其中添加了一个转换器。

我一开始注意到什么,因为什么都没有

  1. 使用
    ui.Image
    加载帧,但会延迟大约 10 秒,然后再使用断点进行调试来显示所有帧。
  2. ui.Image
    转换为
    Uint8List
    所需的时间与帧一样多,但提取完成后格式会显示所有帧。

这是dart pad,可以快速运行和测试它。

如果您想扫描的话,这是代码,我正在从网络加载 gif。

class GifFramesDisplay extends StatefulWidget {
  const GifFramesDisplay({super.key});

  @override
  State<GifFramesDisplay> createState() => _GifFramesDisplayState();
}

class _GifFramesDisplayState extends State<GifFramesDisplay> {
  List<Uint8List> _frames = [];

  @override
  void initState() {
    super.initState();

    loadGif();
  }

  Future<void> loadGif() async {
    // Load the gif image from the network url and store the bytes in a ByteData object
    final url = Uri.parse(
        'https://raw.githubusercontent.com/Aman-Malhotra/animate_icons/main/demo/animate_icons.gif');
    final ByteData data = await NetworkAssetBundle(url).load(url.path);

    // Using the _extractGifFrames function to extract the frames
    _extractGifFrames(data).then((List<Uint8List> frames) {
      // Set the state to update the UI
      setState(() {
        _frames = frames;
      });
    });
  }

  // Function to extract gif image frames
  Future<List<Uint8List>> _extractGifFrames(ByteData data) async {
    // Create a list to store the frames
    final List<Uint8List> frames = <Uint8List>[];

    // Create a codec to decode the gif
    final ui.Codec codec =
        await ui.instantiateImageCodec(data.buffer.asUint8List());

    // Count the number of frames in the gif
    final int frameCount = codec.frameCount;
    print('Total frameCount: $frameCount');

    // Loop through the frames and add them to the list
    for (int i = 0; i < frameCount; i++) {
      // Get the next frame
      final ui.FrameInfo fi = await codec.getNextFrame();

      // Add the frame to the list
      final frame = await loadImage(fi.image);

      // Add the frame to the list if it is not null
      if (frame != null) {
        frames.add(frame);
      }
    }

    return frames;
  }

  Future<Uint8List?> loadImage(ui.Image image) async {
    final ByteData? byteData =
        await image.toByteData(format: ui.ImageByteFormat.png);
    return byteData?.buffer.asUint8List();
  }

  @override
  Widget build(BuildContext context) {
    return Center(
      child: _frames.isEmpty
          ? const CircularProgressIndicator()
          : ListView.builder(
              itemCount: _frames.length,
              itemBuilder: (BuildContext context, int index) {
                return Image.memory(_frames[index]);
              },
            ),
    );
  }
}

单击观看上述代码加载的 gif 帧的预览


0
投票

这里我使用ffmpeg_kit_flutter从GIF中提取所有帧,将所有帧显示在列表中,并单独显示第10帧作为预览。这是我的代码实现:

GIF 提取:FFmpegKit 从资源中存储的 GIF 文件中提取帧并将其保存在临时目录中。 UI:第 10 帧显示在顶部,所有帧都列在下面。

String gifPath = 'assets/bird-dog1.gif'; // GIF asset path
List<String> framePaths = [];
String? tenthFramePath;

Future<void> _extractAllFrames() async {
  try {
    final directory = await getTemporaryDirectory();
    final tempGifPath = '${directory.path}/${gifPath.split('/').last}';
    final byteData = await rootBundle.load(gifPath);
    final gifFile = File(tempGifPath);
    await gifFile.writeAsBytes(byteData.buffer.asUint8List());

final framesDir = '${directory.path}/frames';
final dir = Directory(framesDir);
if (!dir.existsSync()) {
  dir.createSync(recursive: true);
}

String ffmpegCommand = '-y -i $tempGifPath $framesDir/frame_%03d.png';
await FFmpegKit.execute(ffmpegCommand).then((session) async {
  final returnCode = await session.getReturnCode();
  if (ReturnCode.isSuccess(returnCode)) {
    final extractedFrames = dir.listSync().map((file) => file.path).toList();
    setState(() {
      framePaths = extractedFrames;
      if (extractedFrames.length >= 10) {
        tenthFramePath = extractedFrames[9];
      }
    });
  }
});


 } catch (e) {
    print('Error extracting frames: $e');
  }
}

Pubspec.yaml

name: ffmpeflutterproject
description: "A new Flutter project."

publish_to: 'none'

version: 1.0.0+1

environment:
  sdk: ^3.5.0

dependencies:
  flutter:
    sdk: flutter
  ffmpeg_kit_flutter: ^6.0.3

  path_provider:


  cupertino_icons: ^1.0.8

dev_dependencies:
  flutter_test:
    sdk: flutter
  flutter_lints: ^4.0.0

flutter:

  uses-material-design: true

  assets:
    - assets/bird-dog1.gif
    - assets/bird-dog2.gif
    - assets/bird-dog3.gif
    - assets/bird-dog4.gif
© www.soinside.com 2019 - 2024. All rights reserved.