一个图像处理项目在产品化过程中必然涉及用户交互,即用户如何将自己的图片放到项目核心的算法中、如何拿到算法处理的结果,本篇内容涉及常规网络传输的手段。同时也包括使用socket在服务器中不同Python程序间交互的方法,尽管这种业务需求并不常见。

    一般来讲,图像处理算法会使用OpenCV处理numpy矩阵形式的图片,单纯较简单的处理则可以只使用pillow,两种形式可以相互转换;在图像数据传输层面,由我定义的接口通常会将图片编码为base64,以字符串形式同前端交互。

  • 图像二进制流与base64字符串的转换

    当你拿到了前端传给你的base64字符串图片,第一步是将其解码出来,解码之后可以直接得到二进制流;此过程的逆操作,即编码之后可以得到base64,但此时并不是字符串,如果要进行传输,则应当先decode一下。处理base64的时候,应当注意Python的base64不带头,即类似

data:image/jpg;base64,

    的内容,前端处理可以使用正则进行一步包装。

import base64image_b64 = base64.b64decode(image_b64)  # 将base64形式图片解码成二进制流image_b64 = base64.b64encode(image_bin).decode()  # 将二进制流形式的图片编码成字符串
  • 图像二进制流与numpy矩阵的转换

    使用opencv处理图片一定少不了numpy参与

import cv2import numpy as np# 读取一张图片,第二个参数名为flag,可以指定以不同通道等形式读取一张图片,运行结果是一个numpy矩阵形式的图片image = cv2.imread(    "PolyGram_Forever_Live_Concert_in_Singapore(陳慧嫻)_-_37_(14830943344).jpg",    cv2.IMREAD_UNCHANGED)# 将读取出来的图片矩阵转换回二进制流is_succeed, image_encoded = cv2.imencode(".jpg", image)# 跳过文件名,将二进制流形式图片转换为numpy矩阵图片image = cv2.imdecode(np.frombuffer(image_bin, dtype=np.uint8),                     cv2.IMREAD_COLOR)
  • numpy矩阵与pillow图像的转换
import cv2import numpy as npfrom PIL import Imageimage = cv2.imread(    "PolyGram_Forever_Live_Concert_in_Singapore(陳慧嫻)_-_37_(14830943344).jpg",    cv2.IMREAD_UNCHANGED)# pillow形式image_pillow = Image.fromarray(image)# 将pillow图片转换为numpy矩阵image = np.asarray(image_pillow)

以上提到的内容为不同形式的图片之间相互转化的方式,代码整合起来如下

注意:此处代码仅为了演示,实际工程中不要这样写代码!

import argparseimport base64from io import BytesIOfrom socket import AF_INET, SO_REUSEADDR, SOCK_STREAM, SOL_SOCKET, socketimport cv2import numpy as npfrom PIL import Image# base64与opencv、numpy之间转换# 图片转换成base64image = cv2.imread(    "PolyGram_Forever_Live_Concert_in_Singapore(陳慧嫻)_-_37_(14830943344).jpg",    cv2.IMREAD_UNCHANGED)# pillow形式image_pillow = Image.fromarray(image)# 使用pillow将图片转化为二进制流形式image_io = BytesIO()image_pillow.save(image_io, format="png")is_succeed, image_encoded = cv2.imencode(".jpg", image)# base64形式image_b64 = base64.b64encode(image_encoded.tostring()).decode()# bytes流形式# 实际上直接将image_encoded.tostring()写入一个文件中也可以成功,此处显式进行一步转换是为了强调OpenCV的作用image_bytes = BytesIO(image_encoded.tostring())image_bytes.seek(0)  # 此处将指针放到字节流开始处,可以进行读取with open("output.jpg", "wb") as f:    f.write(image_bytes.read())# base64转回图片image_b64 = base64.b64decode(image_b64)  # Python的base64字符串解码不接受带头image_pillow = Image.open(BytesIO(image_b64))# 以下两种等效image_a = cv2.imdecode(np.asarray(bytearray(image_b64), dtype=np.uint8),                       cv2.IMREAD_COLOR)image_b = cv2.imdecode(np.frombuffer(image_b64, dtype=np.uint8),                       cv2.IMREAD_COLOR)def send_array(arr: np.ndarray, dest):    array_send = memoryview(arr).cast('B')    while len(array_send):        size_sent = dest.send(array_send)        print("发送数据大小:", size_sent)        array_send = array_send[size_sent:]def recv_array(arr: np.ndarray, source):    array_recv = memoryview(arr).cast('B')    while len(array_recv):        size_recv = source.recv_into(array_recv)        print("接收数据大小:", size_recv)        array_recv = array_recv[size_recv:]if __name__ == "__main__":    parser = argparse.ArgumentParser()    parser.add_argument("-mode", "--m", choices=("c", "s"), help="服务端还是客户端模式")    args = parser.parse_args()    if args.m == "s":        s = socket(AF_INET, SOCK_STREAM)        s.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)        s.bind(('', 25000))        s.listen(1)        c, a = s.accept()        print("侦测到客户端连结", c)        send_array(image, c)    if args.m == "c":        c = socket(AF_INET, SOCK_STREAM)        c.connect(('localhost', 25000))        a = np.zeros_like(image)        recv_array(a, c)        Image.fromarray(a[::, ::, ::-1]).show()
  • 使用socket进行大型矩阵的传输(以高分辨率图片为例)

    以上代码中包含了服务器(集群)内部传输大型矩阵的范例代码,运行时指定

python this_file.py -m s  # 先以服务端形式开启python this_file.py -m c  # 再以客户端形式开启

此时作为服务端的程序会将图片读取出来,客户端会接收图片并显示出来。整个操作的精髓在

memoryview(arr).cast('B')

memoryview接受一个数组arr并将其转换为一个无符号字节的内存视图,该视图是可变的,同时还支持切片操作,可以直接操作内存中的矩阵而没有数据在内存中复制的操作。

Image.fromarray(a[::, ::, ::-1]).show()

中的奇怪切片操作是因为我使用的测试图片较为特殊,至于为什么选这样一张图测试,一方面是因为这张图版权限制较为宽松

jquery二进制转图片 js图片转二进制流_python base64解码

⬆️️这是测试使用的图片 Lionel Leong from Singapore, Singapore / CC BY-SA (https://creativecommons.org/licenses/by-sa/2.0)

另一方面。。。感受一下

jquery二进制转图片 js图片转二进制流_mfc 二进制转换成图像_02