(五)抠图/抠像服务、图像\视频TCP/IP传输、图像\视频Socket传输、AI实时抠图、AI实时抠像、PaddlePaddle模型、虚拟现实视频会议、沉浸式会议场景、人像去背景、视频背景消除

(五)抠图/抠像服务、图像\视频TCP/IP传输、图像\视频Socket传输、AI实时抠图、AI实时抠像、PaddlePaddle模型、虚拟现实视频会议、沉浸式会议场景、人像去背景、视频背景消除

本文与前几篇博文关联性较强,请事先阅读前几篇。 对此文感兴趣的可以加微深入探讨:herbert156

一、抠图/抠像服务器模式
有时抠图/抠像服务不止是单机使用,需要部署到服务端,给其它的客户端使用,需要利用Socket来进行不同机器之间的图片、视频的传输;此外这种办法也适用于不同编程环境之间的对接,比如服务端用Python部署抠像服务、客户端用其它语言Unity、C/C++、Java等处理抠图之后的图片;

二、服务端处理代码
此函数是服务器端的Python代码, 注释较全,请参考注释即可。

def ReceiveImage():
    address = ('0.0.0.0', 55555)    # IP地址'0.0.0.0'为等待客户端连接
    # 建立socket对象  socket.AF_INET:服务器之间网络通信  socket.SOCK_STREAM:流式socket , for TCP
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.bind(address)     # 将套接字绑定到地址, 在AF_INET下,以元组(host,port)的形式表示地址.
    s.listen(1)     # 开始监听TCP传入连接。参数指定在拒绝连接之前,操作系统可以挂起的最大连接数量。该值至少为1,大部分应用程序5。
    def recvall(sock, count):
        buf = b''  # buf是一个byte类型
        while count:
            newbuf = sock.recv(count)  # 接受TCP套接字的数据。数据以字符串形式返回,count指定要接收的最大数据量.
            if not newbuf: return None
            buf += newbuf
            count -= len(newbuf)
        return buf
    # 接受TCP连接并返回(conn,address),其中conn是新的套接字对象,可以用来接收和发送数据。addr是连接客户端的地址。
    # 没有连接则等待有连接    conn, addr = s.accept()
    conn, addr = s.accept()
    print('Receiving Frame From :【' + addr[0] +':' + str(addr[1])+'】')
    while True:
        try:
            length = recvall(conn, 16)  # 获得图片文件的长度,16代表获取长度
            #print(int(length))
            stringData = recvall(conn, int(length))  # 根据获得的文件长度,获取图片文件
        except:
            print("TCP连接中断,等待客户端重新连接...");
            conn, addr = s.accept()
            print('Receiving Frame From :【' + addr[0] + ':' + str(addr[1]) + '】')
            continue
        try:
            data = np.frombuffer(stringData, np.uint8)  	# 将获取到的字符流数据转换成1维数组
            decimg = cv2.imdecode(data, cv2.IMREAD_COLOR)  	# 将数组解码成图像
        except:
            print("The Date is not image Format!"); continue

        if not Debug_On: ret_img = koutu(decimg)	#调用抠图函数
        else: ret_img = cv2.flip(decimg,1)			#调试时使用:镜像图片
        #ret_img = cv2.imdecode(np.fromfile('test_pic.png', dtype=np.uint8), -1)
        #print(ret_img.shape)
        #cv2.imshow('Server', ret_img)  # 显示图像
        #cv2.waitKey(1)
        encode_param = [int(cv2.IMWRITE_PNG_COMPRESSION), rates]  #rates:1-9
        result, imgencode = cv2.imencode('.png', ret_img, encode_param)
        data1 = np.array(imgencode)
        stringData1 = data1.tobytes()
        conn.send(str.encode(str(len(stringData1)).ljust(16)))
        conn.send(stringData1)  # 发送图片数据
    s.close()

三、客户端处理代码
此函数是客户端的Python代码,也可用其它语言编写,与服务端互通;注释较全,请参考注释即可。
代码中用到的函数(如:CV2toPIL、PILtoCV2、two_pic_combine_PIL等),请在我的其它博文中查找。

import socket,cv2,time,sys
import numpy as np
from PIL import Image

def SendImage(IP = '127.0.0.1', PORT = 55555, src_img = []):
    address = (IP, PORT)        # 建立socket连接,设置连接服务器的IP地址和端口号
    def recvall(sock, count):
        buf = b''  # buf是一个byte类型
        while count:
            newbuf = sock.recv(count)  # 接受TCP套接字的数据,以字符串形式返回,count指定要接收的最大数据量.
            if not newbuf: return None
            buf += newbuf
            count -= len(newbuf)
        return buf

    try:  #建立socket对象;socket.AF_INET:与服务器之间网络通信;socket.SOCK_STREAM:流式socket
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        sock.connect(address)   # 开启socket连接
    except socket.error as msg: print(msg); sys.exit(1)

    while True:
        #frame = cv2.imdecode(np.fromfile(test_file, dtype=np.uint8), -1)
        t1 = time.time()        #计算单帧处理时间

        try:   ret, frame = cap.read()  #读取摄像机的1帧
        except:  print('读取摄像头, 出现错误!'); stop_flag = True; return
        #cv2.imshow("Camera Video (Exit Key: 'q')", frame)  # 显示摄像机原始视频

        # 压缩参数,后面cv2.imencode将会用到,对于jpeg来说,rates越高代表图像质量越好,0-100,默认95
        encode_param = [int(cv2.IMWRITE_JPEG_QUALITY), rates]
        # cv2.imencode将图片格式转换成流数据,赋值到内存缓存中;主要用于图像数据格式的压缩、'.jpg'表示将图片按照jpg格式编码。
        result, imgencode = cv2.imencode('.jpg', frame, encode_param)
        data = np.array(imgencode)  # 建立numpy矩阵
        stringData = data.tobytes() # 将numpy矩阵转换成字符形式,以便在网络中传输
        # 先发送要发送的数据的长度;ljust()方法返回一个原字符串左对齐,并使用空格填充至指定长度的新字符串
        sock.send(str.encode(str(len(stringData)).ljust(16))) #发送图片长度数据
        sock.send(stringData)        # 发送图片内容数据

        length = recvall(sock, 16)  # 接受数据,获得图片文件的长度,16代表获取长度
        stringData1 = recvall(sock, int(length))  # 根据获得的文件长度,获取图片文件
        data = np.frombuffer(stringData1, np.uint8)  # 将获取到的字符流数据转换成1维数组
        ret_img = cv2.imdecode(data, -1)  # 将数组解码成图像:1代表3通道、0代表灰度图像、-1代表4通道
        #print(ret_img.shape)
        print('单帧处理耗时:%.3f秒\b' % (time.time() - t1), end='\r')

        img1 = cv2.resize(bk_img, (size_x, size_y))     #使绿色背景图片大小与摄像机帧大小一致
        img2 = two_pic_combine_PIL(img1, ret_img)       #使带A通道的图片叠加到背景图片以便显示
        img3 = np.concatenate((frame, img2), axis=1)    #合并成1张图,以便对比观察

        cv2.imshow("Removing Background (Exit Key: 'q')", img3)         #显示抠像返回的视频
        if cv2.waitKey(1) == ord('q'): break

    cap.release()   #关闭摄像机
    sock.close()    #关闭Socket

if __name__ == '__main__':
    bk_img = np.zeros((720, 1280, 3), np.uint8)  # 建立黑色空白图片,以便与返回的透明图片合成

    cam_no_str = input("请输入摄像机编号(回车默认0):")      #电脑摄像机的编号:012等等
    if cam_no_str == '': cam_no = 0
    else: cam_no = int(cam_no_str)
    my_ip = input("请输入服务器IP(回车默认本机地址):")
    if  my_ip == '': my_ip = '127.0.0.1'    #'127.0.0.1'是本机环回地址;如果服务器在另外一台电脑,则输入服务器的IP

    try: cap = cv2.VideoCapture(cam_no)  # 读取视频文件
    except: print('读取摄像头, 出现错误...')
    cap.set(3, 640); cap.set(4, 360) #设置摄像机分辨率;不设置的话,就会使用摄像头默认分辨率
    size_x = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))  # 视频流的帧宽度
    size_y = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))  # 视频流的帧高度
    if size_x == 0 or size_y == 0: print('读取摄像头(编号:%d), 出现错误...' % cam_no)

    rates = 75          #网络传输JPEG压缩率
    my_port = 55555      #服务器的接受端口
    print("\n摄像机编号:", cam_no)
    print("服务器IP:",my_ip)
    print("\n在视频窗口里,敲击字母'q'退出程序...\n")
    print("摄像机分辨率:", size_x,'x', size_y,'\n')

    SendImage(IP = my_ip, PORT = my_port)       #调用发送函数

四、效率优化
抠图消耗时间与图片分辨率相关,720p的大概15fps,基本可以满足要求。服务端在传输之前进行了PNG格式的数据压缩,消耗时间很长,影响了实时性。 解决办法:
1、采用多线程处理,抠图的同时进行压缩、传输;
2、采用不压缩方式,直接对原图进行传输,缺点是占用了过多的网络带宽,在本机127.0.0.1对网络无影响,因为数据没有经过网卡,直接环回了。

后记:本文与前几篇博文关联性较强,请事先阅读前几篇。 对此文感兴趣的可以加微深入探讨:herbert156

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值