【问题标题】:How to post an image as a request with Flask Server API in Dart/Flutter?如何在 Dart/Flutter 中使用 Flask Server API 将图像作为请求发布?
【发布时间】:2023-03-18 03:45:01
【问题描述】:

我想向正文中包含图像的基于 Python 的 API 发布请求。我尝试用 5 种方法发送数据:

  1. 等待 http.post()
   final api = Uri.parse("https://e8f628d7.ngrok.io/detections");
   Map<String, dynamic> body = {'images': image};
    final response = await http.post(
      api,
      body: body,
    );

    if (response.statusCode == 200) {
      final responseJson = json.decode(response.body);
      print(responseJson);
    }
  1. Client().post()
     Map<String, dynamic> body = {'images': image};
     var client = new http.Client();
       client.post("https://e8f628d7.ngrok.io/detections",body: body).then((response) {
         print("Post " + response.statusCode.toString());
       });
  1. 迪奥
  2. 多部分请求
    final api = Uri.parse("https://e8f628d7.ngrok.io/detections");
    var stream = new http.ByteStream(DelegatingStream.typed(image.openRead()));
    var length = await image.length();
    var request = new http.MultipartRequest("POST", api);
    var multipartFileSign = new http.MultipartFile(
        'profile_pic', stream, length,
        filename: path.basename(image.path));
    request.files.add(multipartFileSign);
    // send
    var response = await request.send();
    print(response.statusCode);
    response.stream.transform(utf8.decoder).listen((value) {
      print(value);
    });
  1. [DELETED]此问题的第一个答案的链接:
    if (image == null) return;
    String base64Image = base64Encode(image.readAsBytesSync());
    http.post(api, body: {
      'images': base64Image,
    }).then((res) {
      print(res.statusCode);
      print(json.decode(res.body));
    }).catchError((err) {
      print(err);
    });
  }

我能够发送图像并收到 200 成功响应。但是,我不确定图像是否被更改或在发送图像时发生任何问题,因为响应是空的,而它应该有某种响应。 这是我的服务器使用的 app.py:

import time
from absl import app, logging
import cv2
import numpy as np
import tensorflow as tf
from yolov3_tf2.models import (
    YoloV3, YoloV3Tiny
)
from yolov3_tf2.dataset import transform_images, load_tfrecord_dataset
from yolov3_tf2.utils import draw_outputs
from flask import Flask, request, Response, jsonify, send_from_directory, abort
import os

# customize your API through the following parameters
classes_path = './data/labels/coco.names'
weights_path = './weights/yolov3.tf'
tiny = False                    # set to True if using a Yolov3 Tiny model
size = 416                      # size images are resized to for model
output_path = './detections/'   # path to output folder where images with detections are saved
num_classes = 80                # number of classes in model

# load in weights and classes
physical_devices = tf.config.experimental.list_physical_devices('GPU')
if len(physical_devices) > 0:
    tf.config.experimental.set_memory_growth(physical_devices[0], True)

if tiny:
    yolo = YoloV3Tiny(classes=num_classes)
else:
    yolo = YoloV3(classes=num_classes)

yolo.load_weights(weights_path).expect_partial()
print('weights loaded')

class_names = [c.strip() for c in open(classes_path).readlines()]
print('classes loaded')

# Initialize Flask application
app = Flask(__name__)

# API that returns JSON with classes found in images
@app.route('/detections', methods=['POST'])
def get_detections():
    raw_images = []
    images = request.files.getlist("images")
    image_names = []
    for image in images:
        image_name = image.filename
        image_names.append(image_name)
        image.save(os.path.join(os.getcwd(), image_name))
        img_raw = tf.image.decode_image(
            open(image_name, 'rb').read(), channels=3)
        raw_images.append(img_raw)

    num = 0

    # create list for final response
    response = []

    for j in range(len(raw_images)):
        # create list of responses for current image
        responses = []
        raw_img = raw_images[j]
        num+=1
        img = tf.expand_dims(raw_img, 0)
        img = transform_images(img, size)

        t1 = time.time()
        boxes, scores, classes, nums = yolo(img)
        t2 = time.time()
        print('time: {}'.format(t2 - t1))

        print('detections:')
        for i in range(nums[0]):
            print('\t{}, {}, {}'.format(class_names[int(classes[0][i])],
                                            np.array(scores[0][i]),
                                            np.array(boxes[0][i])))
            responses.append({
                "class": class_names[int(classes[0][i])],
                "confidence": float("{0:.2f}".format(np.array(scores[0][i])*100))
            })
        response.append({
            "image": image_names[j],
            "detections": responses
        })
        img = cv2.cvtColor(raw_img.numpy(), cv2.COLOR_RGB2BGR)
        img = draw_outputs(img, (boxes, scores, classes, nums), class_names)
        cv2.imwrite(output_path + 'detection' + str(num) + '.jpg', img)
        print('output saved to: {}'.format(output_path + 'detection' + str(num) + '.jpg'))

    #remove temporary images
    for name in image_names:
        os.remove(name)
    try:
        return jsonify({"response":response}), 200
    except FileNotFoundError:
        abort(404)

# API that returns image with detections on it
@app.route('/image', methods= ['POST'])
def get_image():
    image = request.files["images"]
    image_name = image.filename
    image.save(os.path.join(os.getcwd(), image_name))
    img_raw = tf.image.decode_image(
        open(image_name, 'rb').read(), channels=3)
    img = tf.expand_dims(img_raw, 0)
    img = transform_images(img, size)

    t1 = time.time()
    boxes, scores, classes, nums = yolo(img)
    t2 = time.time()
    print('time: {}'.format(t2 - t1))

    print('detections:')
    for i in range(nums[0]):
        print('\t{}, {}, {}'.format(class_names[int(classes[0][i])],
                                        np.array(scores[0][i]),
                                        np.array(boxes[0][i])))
    img = cv2.cvtColor(img_raw.numpy(), cv2.COLOR_RGB2BGR)
    img = draw_outputs(img, (boxes, scores, classes, nums), class_names)
    cv2.imwrite(output_path + 'detection.jpg', img)
    print('output saved to: {}'.format(output_path + 'detection.jpg'))

    # prepare image for response
    _, img_encoded = cv2.imencode('.png', img)
    response = img_encoded.tostring()

    #remove temporary image
    os.remove(image_name)

    try:
        return Response(response=response, status=200, mimetype='image/png')
    except FileNotFoundError:
        abort(404)
if __name__ == '__main__':
    app.run(debug=True, host = '0.0.0.0', port=5000)

我尝试直接通过 Postman 发送相同的图像并获得所需的响应,但是当我使用颤振应用程序执行此操作时,我没有得到它。图像是否有可能被更改或修改?而且,除了上述3种方法之外,还有其他方法可以将图像发送到API吗?

【问题讨论】:

  • 通常会与多部分请求一起发送。我没有看到您的代码尝试这样做。
  • @RichardHeap 感谢您的关注。我确实使用了多部分请求。我已经更新了我的问题!
  • 请重构您的多部分代码以使用这种方便的方法:pub.dev/documentation/http/latest/http/MultipartFile/…,它删除了大约 4 行。然后让我们知道结果。
  • @RichardHeap MultipartFile.fromPath() 不起作用。没有这样的功能。我已经检查过了。 MutipartFile.fromBytes() 和 MultipartFile.fromString() 是唯一的函数。

标签: api flutter dart


【解决方案1】:

您需要确保您使用的是良好版本的http。最近有一个回归打破了多部分形式。目前最安全的做法是在 pubspec.yaml 中硬编码确切的版本(您可能想查看 pubspec.lock 以查看您使用的版本以确认它是有错误的版本之一。)

  http: 0.12.0+4

然后试试这个:

main() async {
  http.MultipartRequest request = http.MultipartRequest('POST', Uri.parse(url));

  request.files.add(
    await http.MultipartFile.fromPath(
      'images',
      File('kitten1.jpg').path,
      contentType: MediaType('application', 'jpeg'),
    ),
  );

  http.StreamedResponse r = await request.send();
  print(r.statusCode);
  print(await r.stream.transform(utf8.decoder).join());
}

【讨论】:

  • 这对我有用。太感谢了。我正在检查代码 new http.Multipart.fromPath() 但后来我注意到我们需要等待才能使用 http.MultipartFile.fromPath。非常感谢!!!!
  • 确实如此。 FromPath 是一个静态的,而不是一个命名的构造函数,所以你不会使用 new 。事实上,您应该在任何地方都删除新关键字以获得更好的样式。
  • 如果我的回复中有图片怎么办?我的服务器使用 image.toString() 发送图像。所以, print(r.stream.transform(utf8.decoder).join()) 给了我糟糕的 UTF-8 错误。
  • 为什么要将图像编码为字符串?你想从服务器返回什么?可能最好针对您遇到的问题提出一个新问题。
  • 是的,你是对的。我会调查的。感谢您在 2 天内提供的所有帮助。
猜你喜欢
  • 1970-01-01
  • 2022-11-18
  • 2020-01-03
  • 2018-12-12
  • 2021-01-26
  • 2017-04-12
  • 1970-01-01
  • 1970-01-01
  • 2020-01-30
相关资源
最近更新 更多