将 Numpy 数组图像编码为图像类型(.png 等)以将其与 GCloud Vision API 一起使用 - 没有 OpenCV
Encoding a Numpy Array Image to an Image type (.png etc.) to use it with the GCloud Vision API - without OpenCV
在决定不使用 OpenCV 之后,因为我只使用它的一个函数,所以我想用其他函数替换 cv2.imencode()
函数。目标是将 2D Numpy Array 转换为图像格式(如 .png)以将其发送到 GCloud Vision API .
这是我一直在用的:
content = cv2.imencode('.png', image)[1].tostring()
image = vision.types.Image(content=content)
现在我希望在不使用 OpenCV 的情况下实现相同的目标。
到目前为止我发现的东西:
- 视觉API需要base64编码数据
- Imencode returns 特定图像类型的编码字节
我认为值得注意的是我的 numpy 数组是一个只有 2 维的二进制图像,整个函数将在 API 中使用,因此将 png 保存到磁盘和重装是要避免的。
纯PNG写手Python
如果您坚持使用或多或少的纯 python,ideasman 对 this question 的回答中的以下功能很有用。
def write_png(buf, width, height):
""" buf: must be bytes or a bytearray in Python3.x,
a regular string in Python2.x.
"""
import zlib, struct
# reverse the vertical line order and add null bytes at the start
width_byte_4 = width * 4
raw_data = b''.join(
b'\x00' + buf[span:span + width_byte_4]
for span in range((height - 1) * width_byte_4, -1, - width_byte_4)
)
def png_pack(png_tag, data):
chunk_head = png_tag + data
return (struct.pack("!I", len(data)) +
chunk_head +
struct.pack("!I", 0xFFFFFFFF & zlib.crc32(chunk_head)))
return b''.join([
b'\x89PNG\r\n\x1a\n',
png_pack(b'IHDR', struct.pack("!2I5B", width, height, 8, 6, 0, 0, 0)),
png_pack(b'IDAT', zlib.compress(raw_data, 9)),
png_pack(b'IEND', b'')])
将Numpy数组写入PNG格式字节字面量,编码为base64
为了将灰度图像表示为 RGBA 图像,我们将矩阵堆叠成 4 个通道并设置 alpha 通道。 (假设您的 2d numpy 数组称为 "img")。由于 PNG 坐标的工作方式,我们还垂直翻转了 numpy 数组。
import base64
img_rgba = np.flipud(np.stack((img,)*4, axis=-1)) # flip y-axis
img_rgba[:, :, -1] = 255 # set alpha channel (png uses byte-order)
data = write_png(bytearray(img_rgba), img_rgba.shape[1], img_rgba.shape[0])
data_enc = base64.b64encode(data)
测试编码是否正常工作
最后,为确保编码有效,我们解码 base64 字符串并将输出写入磁盘为 "test_out.png"。检查这是否与您开始使用的图像相同。
with open("test_out.png", "wb") as fb:
fb.write(base64.decodestring(data_enc))
备选方案:只需使用 PIL
但是,我假设您首先使用某些库来实际读取您的图像? (除非你正在生成它们)。大多数用于读取图像的库都支持这种东西。假设您正在使用 PIL,您还可以尝试以下代码片段 ()。它只是将文件保存在内存中,而不是磁盘上,并使用它来生成 base64 字符串。
in_mem_file = io.BytesIO()
img.save(in_mem_file, format = "PNG")
# reset file pointer to start
in_mem_file.seek(0)
img_bytes = in_mem_file.read()
base64_encoded_result_bytes = base64.b64encode(img_bytes)
base64_encoded_result_str = base64_encoded_result_bytes.decode('ascii')
在决定不使用 OpenCV 之后,因为我只使用它的一个函数,所以我想用其他函数替换 cv2.imencode()
函数。目标是将 2D Numpy Array 转换为图像格式(如 .png)以将其发送到 GCloud Vision API .
这是我一直在用的:
content = cv2.imencode('.png', image)[1].tostring()
image = vision.types.Image(content=content)
现在我希望在不使用 OpenCV 的情况下实现相同的目标。
到目前为止我发现的东西:
- 视觉API需要base64编码数据
- Imencode returns 特定图像类型的编码字节
我认为值得注意的是我的 numpy 数组是一个只有 2 维的二进制图像,整个函数将在 API 中使用,因此将 png 保存到磁盘和重装是要避免的。
纯PNG写手Python
如果您坚持使用或多或少的纯 python,ideasman 对 this question 的回答中的以下功能很有用。
def write_png(buf, width, height):
""" buf: must be bytes or a bytearray in Python3.x,
a regular string in Python2.x.
"""
import zlib, struct
# reverse the vertical line order and add null bytes at the start
width_byte_4 = width * 4
raw_data = b''.join(
b'\x00' + buf[span:span + width_byte_4]
for span in range((height - 1) * width_byte_4, -1, - width_byte_4)
)
def png_pack(png_tag, data):
chunk_head = png_tag + data
return (struct.pack("!I", len(data)) +
chunk_head +
struct.pack("!I", 0xFFFFFFFF & zlib.crc32(chunk_head)))
return b''.join([
b'\x89PNG\r\n\x1a\n',
png_pack(b'IHDR', struct.pack("!2I5B", width, height, 8, 6, 0, 0, 0)),
png_pack(b'IDAT', zlib.compress(raw_data, 9)),
png_pack(b'IEND', b'')])
将Numpy数组写入PNG格式字节字面量,编码为base64
为了将灰度图像表示为 RGBA 图像,我们将矩阵堆叠成 4 个通道并设置 alpha 通道。 (假设您的 2d numpy 数组称为 "img")。由于 PNG 坐标的工作方式,我们还垂直翻转了 numpy 数组。
import base64
img_rgba = np.flipud(np.stack((img,)*4, axis=-1)) # flip y-axis
img_rgba[:, :, -1] = 255 # set alpha channel (png uses byte-order)
data = write_png(bytearray(img_rgba), img_rgba.shape[1], img_rgba.shape[0])
data_enc = base64.b64encode(data)
测试编码是否正常工作
最后,为确保编码有效,我们解码 base64 字符串并将输出写入磁盘为 "test_out.png"。检查这是否与您开始使用的图像相同。
with open("test_out.png", "wb") as fb:
fb.write(base64.decodestring(data_enc))
备选方案:只需使用 PIL
但是,我假设您首先使用某些库来实际读取您的图像? (除非你正在生成它们)。大多数用于读取图像的库都支持这种东西。假设您正在使用 PIL,您还可以尝试以下代码片段 (
in_mem_file = io.BytesIO()
img.save(in_mem_file, format = "PNG")
# reset file pointer to start
in_mem_file.seek(0)
img_bytes = in_mem_file.read()
base64_encoded_result_bytes = base64.b64encode(img_bytes)
base64_encoded_result_str = base64_encoded_result_bytes.decode('ascii')