【问题标题】:Sending image as numpy array over MQTT to AWS IoT Core通过 MQTT 将图像作为 numpy 数组发送到 AWS IoT Core
【发布时间】:2021-05-30 10:16:09
【问题描述】:

我正在从网络摄像头捕获图像,并使用 AWS IoT Device Python SDK 将这些图像作为 numpy 数组发送到 AWS IoT Core。在 AWS IoT Core,这个 numpy 数组被传输到 lamda 函数进行图像处理(通过 RESNET 模型)。

但是 numpy 数组的大小太大而无法通过 MQTT 发送,这就是 AWS IoT Core 没有接收到它的原因。

所以我的问题是:

  1. 有没有办法增加 SDK 中的 MQTT TX 缓冲区大小?我找不到。
  2. 如果不是,那么通过 MQTT 发送大型 numpy 数组并接收它的最佳方法是什么?

发布代码:

    cam = cv2.VideoCapture(0)
    ret, frame = cam.read()
    message['image'] = frame.tolist() #numpy array converted to list to make it work with json.dumps
    messageJson = json.dumps(message)
    myAWSIoTMQTTClient.publish(topic, messageJson, 0)

帧 numpy 数组:

[0:480] :[array([[156, 168, 20...ype=uint8), array([[155, 167, 20...ype=uint8), array([[144, 168, 20...ype=uint8), array([[144, 168, 20...ype=uint8), array([[138, 168, 20...ype=uint8), array([[138, 168, 20...ype=uint8), array([[149, 170, 20...ype=uint8), array([[151, 172, 20...ype=uint8), array([[156, 174, 20...ype=uint8), array([[156, 174, 20...ype=uint8), array([[153, 174, 20...ype=uint8), array([[152, 173, 20...ype=uint8), array([[153, 172, 20...ype=uint8), array([[154, 173, 20...ype=uint8), ...]
dtype:dtype('uint8')
max:222
min:0
shape:(480, 640, 3)
size:921600

【问题讨论】:

  • 阵列有多大? MQTT 的最大有效负载大小为 256mb,AWS 的限制可能更小
  • 我已经添加了有问题的数组信息
  • 是的,但是转换成 JSON 后有多大?我们已经是 5mb 纯粹作为原始字节,将其转换为 JSON 表示可以轻松增加一个或多个数量级。
  • 您能否利用 JPEG 压缩并发送 JPEG 图像并将其重新膨胀到接收端的 Numpy 数组?这可能是大小的 1/10 或更少。或者你可以使用一半大小的 YUV 吗?

标签: python-3.x amazon-web-services image-processing mqtt aws-iot


【解决方案1】:

你似乎有点卡住了。我不了解 AWS 方面的情况,但可以向您展示成像方面。我建议您将图像 frame(这是一个 Numpy 数组)转换为 JPEG 图像进行传输,因为这将占用更少的带宽并导致更小的 MQTT 消息。但是,JPEG 是二进制的,因此您不能将其作为 JSON 发送,除非您先对其进行 base64 编码。在下面的示例中,我展示了一种获取图像的方法(Numpy 数组),并且:

  • 将其转换为 JPEG。请注意,您同样可以使用 PNG - 特别是如果您想要无损 - 但通常会更慢。请注意,您同样可以使用 PIL/Pillow 而不是 OpenCV。请注意,您也可以改变质量,这会影响尺寸。
  • base64 编码
  • 转换为 JSON

然后我显示接收端的相反过程。实际的大小/带宽节省取决于您的图像以及它的可压缩程度以及您准备接受的质量损失,但是我从 920kB 的图像开始,实际上用 66kB 的 JSON 表示它。

#!/usr/bin/env python3

import cv2
import numpy as np
from base64 import b64encode

# Get any old image, 640x480 pixels - corresponds to your "frame"
na = cv2.imread('start.jpg', cv2.IMREAD_COLOR)
print(f'DEBUG: Size as Numpy array: {na.nbytes}')

# Convert to "in-memory" JPEG
_, JPEG = cv2.imencode(".jpg", na, [int(cv2.IMWRITE_JPEG_QUALITY), 80])
print(f'DEBUG: Size as JPEG: {JPEG.nbytes}')
JPEG.tofile('DEBUG-original.jpg')

# Base64 encode
b64 = b64encode(JPEG)
print(f'DEBUG: Size as base64: {len(b64)}')
print(f'DEBUG: Start of base64: {b64[:32]}...')

# JSON-encode
message = { "image": b64.decode("utf-8") }
messageJSON = json.dumps(message)
print(f'DEBUG: Start of JSON: {messageJSON[:32]}')

样本输出

DEBUG: Size as Numpy array: 921600
DEBUG: Size as JPEG: 49456
DEBUG: Size as base64: 65944
DEBUG: Start of base64: b'/9j/4AAQSkZJRgABAQAAAQABAAD/2wBD'...
DEBUG: Start of JSON: {"image": "/9j/4AAQSkZJRgABAQAAA

接收方将如下所示:

### RECEIVER: All variables suffixed with '_r' to denote receiver ###

import cv2
import numpy as np
from base64 import b64decode

# Extract base64 from JSON
b64_r = json.loads(messageJSON)

# Extract JPEG-encoded image from base64-encoded string
JPEG_r = b64decode(b64_r["image"])

# Decode JPEG back into Numpy array
na_r = cv2.imdecode(np.frombuffer(JPEG_r,dtype=np.uint8), cv2.IMREAD_COLOR)

注意:如果你想转换成灰度,你需要在之前 JPEG编码:

# Convert to greyscale
grey = cv2.cvtColor(na, cv2. COLOR_BGR2GRAY)

# JPEG-encode
_, JPEG = cv2.imencode(".jpg", grey, [int(cv2.IMWRITE_JPEG_QUALITY), 80])

关键字:Python、MQTT、AWS、图像处理、JSON、base64、编码、解码、带宽、最小化、最小化、减少、素数。

【讨论】:

  • 非常感谢!我真的被这个问题困住了。您的解决方案完美运行。
  • 酷 - 很高兴它对你有用。祝你的项目好运!如果再次卡住,请回来。问题和答案都是免费的:-)
猜你喜欢
  • 2022-06-12
  • 1970-01-01
  • 1970-01-01
  • 2018-11-26
  • 2019-01-13
  • 1970-01-01
  • 2021-01-15
  • 1970-01-01
相关资源
最近更新 更多