场景
我想通过json传输图片, 那么必须把图片转为string格式.
一般都是img->byte->string->byte->img的流程
四种方式的实现与比较
base64将img变为bytes有两种方式:b64encode
和encodestring
(虽然名字中有string, 但是转出来人然是bytes)
bytes转string有两种方式:decode("gbk")
和decode("utf-8")
一共有四种组合, 黑盒测试他们的时间开销.
import base64
from time import time
from pprint import pprint
img_input = open("input.jpeg","rb").read()
res = []
T = time()
x = base64.b64encode(img_input).decode("gbk")
y = base64.decodebytes(x.encode("gbk"))
res.append((time()-T,len(x), "b64encode_gbk"))
T = time()
x = base64.b64encode(img_input).decode("utf-8")
y = base64.decodebytes(x.encode("utf-8"))
res.append((time()-T,len(x), "b64encode_utf-8"))
T = time()
x = base64.encodestring(img_input).decode("gbk")
y = base64.decodestring(x.encode("gbk"))
res.append((time()-T,len(x), "encodestring_gbk"))
T = time()
x = base64.encodestring(img_input).decode("utf-8")
y = base64.decodestring(x.encode("utf-8"))
res.append((time()-T,len(x), "encodestring_utf-8"))
print("时间开销从小到大:")
pprint(sorted(res, key=lambda x:x[0]))
print("字符串长度开销从小到大:")
pprint(sorted(res, key=lambda x:x[1]))
时间开销从小到大:
[(2.5272369384765625e-05, 6568, 'b64encode_utf-8'),
(4.887580871582031e-05, 6655, 'encodestring_utf-8'),
(0.0002677440643310547, 6655, 'encodestring_gbk'),
(0.0004775524139404297, 6568, 'b64encode_gbk')]
字符串长度开销从小到大:
[(0.0004775524139404297, 6568, 'b64encode_gbk'),
(2.5272369384765625e-05, 6568, 'b64encode_utf-8'),
(0.0002677440643310547, 6655, 'encodestring_gbk'),
(4.887580871582031e-05, 6655, 'encodestring_utf-8')]
为什么用base64
为了把图片转换为string, 我尝试了如下方法:
- open(“xxx.jpeg”).read() 得到jpeg格式的bytes, 但是没有找到怎么转为为array的形式
- cv2.imread(“xxx.jpeg”,1)的道array, 用array.tobytes()转换维bytes, 再用decode(“utf-8”)编码为字符串, 但是报错说该bytes以某个byte开头,不能够进行转换, 用gbk也是如此
- 所以现在只能是cv2.imread(“xxx.jpeg”,1)得到array, 用base64.b64encode()转为bytes, 再用decode(“utf-8”)转为字符串
测试代码如下:
import numpy as np
import cv2
from PIL import Image
import base64
# 得到array
array_3d = cv2.imread("input.jpeg",1)
print("得到三维array_3d", type(array_3d), array_3d.shape)
array_1d = array_3d.ravel() #转换维一维
print("得到一维array_1d", type(array_1d), array_1d.shape)
bytes_ = base64.b64encode(array_1d)
print("得到bytes",type(bytes_))
string = bytes_.decode("utf-8")
print("得到string", type(string))
# 此处进行json传输
bytes_recv = string.encode("utf-8")
print("还原bytes", type(bytes_recv))
bytes_decode_recv = base64.decodebytes(bytes_recv)
print("解码后的bytes", type(bytes_decode_recv))
array_1d_recv = np.frombuffer(bytes_decode_recv,dtype=np.uint8)
print("还原一维array", type(array_1d_recv), array_1d_recv.shape)
array_3d_recv = array_1d_recv.reshape(*array_3d.shape)
print("还原三维array", type(array_3d_recv), array_3d_recv.shape)
cv2.imshow("",array_3d_recv)
cv2.waitKey(0)
如果正常, 则显示原来的图片
再次更新
前面的方法虽然能够转为string, 但是效率很低, 再传输前应该先压缩.
对于jpeg的bytes, 可以先转化为bytes buffer, 然后再通过PIL.Image.open(tempbuffer)得到array
测试代码如下:
from PIL import Image
import base64
from io import BytesIO
# 得到array
bytes_ = open("input.jpeg","rb").read()
print("得到bytes", type(bytes_))
string = base64.b64encode(bytes_).decode("utf-8")
print("得到string", type(string))
print("传输string")
bytes_recv = base64.decodebytes(string.encode("utf-8"))
print("解码为bytes",type(bytes_recv) )
tempBuff = BytesIO()
tempBuff.write(bytes_recv)
tempBuff.seek(0)
print("创建byte buffer", type(tempBuff))
img_recv = Image.open(tempBuff)
print("得到image", type(img_recv))
再次更新
如果一开始获取的就是array的话. 测试代码如下:
import cv2
import numpy as np
import io
import base64
img_array = cv2.imread("input.jpeg",1)
print(type(img_array))
img_jpeg = cv2.imencode(".jpeg", img_array)[1].tostring()
print(type(img_jpeg))
string = base64.b64encode(img_jpeg).decode("utf-8")
print(type(string), len(string))
img_jpeg = base64.decodebytes(string.encode("utf-8"))
print(type(img_jpeg))
img_array = cv2.imdecode(np.fromstring(img_jpeg, dtype=np.uint8),1)
print(type(img_array))
cv2.imshow("",img_array)
cv2.waitKey(0)