【问题标题】:Flutter compute function for image hashing用于图像哈希的 Flutter 计算函数
【发布时间】:2019-12-14 14:02:31
【问题描述】:

hii 我正在尝试实现计算功能。我正在尝试使用 image_picker 插件通过相机单击图像。然后我试图获取图像文件的哈希摘要。由于这是一项非常激烈的操作,我正在尝试使用计算功能但无法这样做。 这就是我到目前为止所做的事情

final File picture = await ImagePicker.pickImage(
        source: ImageSource.camera);
    setState(() {
      _imageFile = picture;
    });
var result = await compute(generateImageHash(), _imageFile);
    print(result);

这是我试图传递给计算函数的 generateImageHash 函数

generateImageHash() async{
    var image_bytes =  _imageFile.readAsBytesSync().toString();
    var bytes = utf8.encode(image_bytes); // data being hashed
    String digest = sha256.convert(bytes).toString();
    print("This is image Digest :  $digest");
    return digest;
  }

但是当我使用手机点击图片时,我收到以下错误

 Unhandled Exception: type 'Future<dynamic>' is not a subtype of type '(File) => FutureOr<dynamic>'

我是强类型语言的新手,请帮助。谢谢

【问题讨论】:

    标签: asynchronous flutter strong-typing


    【解决方案1】:

    第一期请改

    var result = await compute(generateImageHash(), _imageFile); 
    

    var result = await compute(generateImageHash, _imageFile);
    

    你不需要这个 ()
    类似https://github.com/flutter/flutter/issues/27582

    第二个问题请从计算中删除 await 关键字

    var result = compute(generateImageHash, _imageFile);
    

    你需要修改

    Future<String> generateImageHash(File file) async{
    

    放到课外

    我只是提供完整的工作代码和演示图片。 您可以在这张图片中看到摘要字符串

    // Copyright 2017 The Chromium Authors. All rights reserved.
    // Use of this source code is governed by a BSD-style license that can be
    // found in the LICENSE file.
    
    import 'dart:async';
    import 'dart:io';
    
    import 'package:flutter/material.dart';
    import 'package:image_picker/image_picker.dart';
    import 'package:video_player/video_player.dart';
    import 'dart:async';
    import 'dart:convert';
    import 'package:flutter/foundation.dart';
    import 'package:crypto/crypto.dart';
    
    void main() {
      runApp(MyApp());
    }
    
    class MyApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          title: 'Image Picker Demo',
          home: MyHomePage(title: 'Image Picker Example'),
        );
      }
    }
    
    class MyHomePage extends StatefulWidget {
      MyHomePage({Key key, this.title}) : super(key: key);
    
      final String title;
    
      @override
      _MyHomePageState createState() => _MyHomePageState();
    }
    
    Future<String> generateImageHash(File file) async{
      var image_bytes =  file.readAsBytesSync().toString();
      var bytes = utf8.encode(image_bytes); // data being hashed
      String digest = sha256.convert(bytes).toString();
      print("This is image Digest :  $digest");
      return  digest;
    }
    
    class _MyHomePageState extends State<MyHomePage> {
      File _imageFile;
      dynamic _pickImageError;
      bool isVideo = false;
      VideoPlayerController _controller;
      String _retrieveDataError;
    
    
    
      void _onImageButtonPressed(ImageSource source) async {
        if (_controller != null) {
          _controller.setVolume(0.0);
          _controller.removeListener(_onVideoControllerUpdate);
        }
        if (isVideo) {
          ImagePicker.pickVideo(source: source).then((File file) {
            if (file != null && mounted) {
              setState(() {
                _controller = VideoPlayerController.file(file)
                  ..addListener(_onVideoControllerUpdate)
                  ..setVolume(1.0)
                  ..initialize()
                  ..setLooping(true)
                  ..play();
              });
            }
          });
        } else {
          try {
            print("_imageFile start");
            _imageFile = await ImagePicker.pickImage(source: source);
            print("_imageFile end");
    
            print("compute start");
            var result = compute(generateImageHash, _imageFile);
            print("compute end");
            print(result);
          } catch (e) {
            _pickImageError = e;
          }
          setState(() {});
        }
      }
    
      void _onVideoControllerUpdate() {
        setState(() {});
      }
    
      @override
      void deactivate() {
        if (_controller != null) {
          _controller.setVolume(0.0);
          _controller.removeListener(_onVideoControllerUpdate);
        }
        super.deactivate();
      }
    
      @override
      void dispose() {
        if (_controller != null) {
          _controller.dispose();
        }
        super.dispose();
      }
    
      Widget _previewVideo(VideoPlayerController controller) {
        final Text retrieveError = _getRetrieveErrorWidget();
        if (retrieveError != null) {
          return retrieveError;
        }
        if (controller == null) {
          return const Text(
            'You have not yet picked a video',
            textAlign: TextAlign.center,
          );
        } else if (controller.value.initialized) {
          return Padding(
            padding: const EdgeInsets.all(10.0),
            child: AspectRatioVideo(controller),
          );
        } else {
          return const Text(
            'Error Loading Video',
            textAlign: TextAlign.center,
          );
        }
      }
    
      Widget _previewImage() {
        final Text retrieveError = _getRetrieveErrorWidget();
        if (retrieveError != null) {
          return retrieveError;
        }
        if (_imageFile != null) {
          return Image.file(_imageFile);
        } else if (_pickImageError != null) {
          return Text(
            'Pick image error: $_pickImageError',
            textAlign: TextAlign.center,
          );
        } else {
          return const Text(
            'You have not yet picked an image.',
            textAlign: TextAlign.center,
          );
        }
      }
    
      Future<void> retrieveLostData() async {
        final LostDataResponse response = await ImagePicker.retrieveLostData();
        if (response.isEmpty) {
          return;
        }
        if (response.file != null) {
          setState(() {
            if (response.type == RetrieveType.video) {
              isVideo = true;
              _controller = VideoPlayerController.file(response.file)
                ..addListener(_onVideoControllerUpdate)
                ..setVolume(1.0)
                ..initialize()
                ..setLooping(true)
                ..play();
            } else {
              isVideo = false;
              _imageFile = response.file;
            }
          });
        } else {
          _retrieveDataError = response.exception.code;
        }
      }
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text(widget.title),
          ),
          body: Center(
            child: Platform.isAndroid
                ? FutureBuilder<void>(
              future: retrieveLostData(),
              builder: (BuildContext context, AsyncSnapshot<void> snapshot) {
                switch (snapshot.connectionState) {
                  case ConnectionState.none:
                  case ConnectionState.waiting:
                    return const Text(
                      'You have not yet picked an image.',
                      textAlign: TextAlign.center,
                    );
                  case ConnectionState.done:
                    return isVideo
                        ? _previewVideo(_controller)
                        : _previewImage();
                  default:
                    if (snapshot.hasError) {
                      return Text(
                        'Pick image/video error: ${snapshot.error}}',
                        textAlign: TextAlign.center,
                      );
                    } else {
                      return const Text(
                        'You have not yet picked an image.',
                        textAlign: TextAlign.center,
                      );
                    }
                }
              },
            )
                : (isVideo ? _previewVideo(_controller) : _previewImage()),
          ),
          floatingActionButton: Column(
            mainAxisAlignment: MainAxisAlignment.end,
            children: <Widget>[
              FloatingActionButton(
                onPressed: () {
                  isVideo = false;
                  _onImageButtonPressed(ImageSource.gallery);
                },
                heroTag: 'image0',
                tooltip: 'Pick Image from gallery',
                child: const Icon(Icons.photo_library),
              ),
              Padding(
                padding: const EdgeInsets.only(top: 16.0),
                child: FloatingActionButton(
                  onPressed: () {
                    isVideo = false;
                    _onImageButtonPressed(ImageSource.camera);
                  },
                  heroTag: 'image1',
                  tooltip: 'Take a Photo',
                  child: const Icon(Icons.camera_alt),
                ),
              ),
              Padding(
                padding: const EdgeInsets.only(top: 16.0),
                child: FloatingActionButton(
                  backgroundColor: Colors.red,
                  onPressed: () {
                    isVideo = true;
                    _onImageButtonPressed(ImageSource.gallery);
                  },
                  heroTag: 'video0',
                  tooltip: 'Pick Video from gallery',
                  child: const Icon(Icons.video_library),
                ),
              ),
              Padding(
                padding: const EdgeInsets.only(top: 16.0),
                child: FloatingActionButton(
                  backgroundColor: Colors.red,
                  onPressed: () {
                    isVideo = true;
                    _onImageButtonPressed(ImageSource.camera);
                  },
                  heroTag: 'video1',
                  tooltip: 'Take a Video',
                  child: const Icon(Icons.videocam),
                ),
              ),
            ],
          ),
        );
      }
    
      Text _getRetrieveErrorWidget() {
        if (_retrieveDataError != null) {
          final Text result = Text(_retrieveDataError);
          _retrieveDataError = null;
          return result;
        }
        return null;
      }
    }
    
    class AspectRatioVideo extends StatefulWidget {
      AspectRatioVideo(this.controller);
    
      final VideoPlayerController controller;
    
      @override
      AspectRatioVideoState createState() => AspectRatioVideoState();
    }
    
    class AspectRatioVideoState extends State<AspectRatioVideo> {
      VideoPlayerController get controller => widget.controller;
      bool initialized = false;
    
      void _onVideoControllerUpdate() {
        if (!mounted) {
          return;
        }
        if (initialized != controller.value.initialized) {
          initialized = controller.value.initialized;
          setState(() {});
        }
      }
    
      @override
      void initState() {
        super.initState();
        controller.addListener(_onVideoControllerUpdate);
      }
    
      @override
      Widget build(BuildContext context) {
        if (initialized) {
          return Center(
            child: AspectRatio(
              aspectRatio: controller.value?.aspectRatio,
              child: VideoPlayer(controller),
            ),
          );
        } else {
          return Container();
        }
      }
    }
    

    【讨论】:

    • 感谢您的回复,但如果我这样做,我会收到此错误:参数类型 'dynamic Function()' can't be assigned to the parameter type 'FutureOr Function(File)' .
    • 是的..我试过按照这个例子..但无法实现
    • 需要解释的太多了。所以我只是提供一个完整的工作代码,让你直接修改它。并删除一些我没用的 cmets
    • 非常感谢兄弟的痛苦..我非常感激..我会尝试实现你的代码并告诉你..只是想知道性能如何?如果没有计算,渲染图像大约需要 10 秒
    • 我提供的工作代码是官方 image_picker 示例。我只是添加你的逻辑。关于性能,在我的 Android 模拟器中不到 3 秒
    【解决方案2】:

    我还建议您将计算哈希的函数代码更改为:

    Future<String> generateImageHash(File file) async{
      Digest digest = await sha256.bind(file).openRead()).first;
      print("This is image Digest :  ${digest.toString()}");
      return  digest.toString();
    }
    

    否则您的摘要将被错误计算。

    【讨论】:

      【解决方案3】:

      试试这些代码。

      static  generateImageHash(String fileBytes){
      print('fileBytes = $fileBytes');
      var bytes = utf8.encode(fileBytes); // data being hashed
      String digest = sha256.convert(bytes).toString();
      print("This is image Digest :  $digest");
      return digest;
      }
      
      
      
      var imageBytes =  image.readAsBytesSync().toString();
      var result = await compute(generateImageHash, imageBytes);
      

      【讨论】:

      • 当我尝试执行此操作时,会出现此错误:无法将参数类型 'Future Function()' 分配给参数类型 'FutureOr Function(File) '。
      猜你喜欢
      • 2017-12-26
      • 1970-01-01
      • 2020-09-24
      • 2016-06-23
      • 2017-10-28
      • 1970-01-01
      • 1970-01-01
      • 2010-11-03
      • 2019-10-03
      相关资源
      最近更新 更多