python OpenCV 模拟远程视频监控(tcp协议)

利用OpenCV对视频进行操作 , 再利用 TCP协议 创建服务端和客户端 ,完成视频的远程传输…

tcp传输视频数据和tcp收发信息流程差不多


tcp客户端 :

1 init方法完成套接字的初始化,传入地址和端口,设置视频的分辨率
2 connect方法 利用客户端的socket对象的connect方法 连接服务器
3 send方法 读取摄像头拍摄的数据, ( socekt对象只发送二进制的数据 ) 把数据处理成二进制 , struct包的pack方法把里面的参数打包好自动转成二进制 , 再利用send发送出去

客户端完整代码:

import socket
import struct
import time
import traceback

import cv2
import numpy


class Client(object):
    """客户端"""

    def __init__(self, addr_port=('192.168.52.1', 11000)):
        # 连接的服务器的地址
        # 连接的服务器的端口
        self.addr_port = addr_port
        # 创建套接字
        self.client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        # 地址端口可以复用
        self.client.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

        # 分辨率
        self.resolution = (640, 480)

    def connect(self):
        """链接服务器"""
        try:
            self.client.connect(self.addr_port)
            return True
        except Exception as e:
            traceback.print_exc()  # 打印原始的异常信息
            print('连接失败')
            return False

    def send2server(self):
        """读摄像头数据 发送给服务器"""
        camera = cv2.VideoCapture(0)  # 摄像头对象
        print('isOpened:', camera.isOpened())

        while camera.isOpened():

            try:
                # 获取摄像头数据
                ret, frame = camera.read()
                # 对每一帧图片做大小处理 和大小的压缩
                frame = cv2.resize(frame, self.resolution)
                # 参1图片后缀名 参2 原图片的数据 参3图片质量 0-100 越大越清晰
                ret, img = cv2.imencode('.jpg', frame, [cv2.IMWRITE_JPEG_QUALITY, 100])
                # img 是被压缩后的数据 无法正常显示
                print(img)
                print('-------------')
                print(img.tostring())

                # 转换为numpy格式数据
                img_code = numpy.array(img)

                # 转为二进制数据
                img = img_code.tostring()

                # 获取数据长度
                length = len(img)

                # 发送的数据  大小 宽 高 图片数据

                # 数据打包变为二进制
                # pack方法参数1 指定打包数据的数据大小  i 4字节 h代表2字节
                all_data = struct.pack('ihh', length, self.resolution[0], self.resolution[1]) + img

                self.client.send(all_data)
                time.sleep(0.01)
            except:
                camera.release()  # 释放摄像头
                traceback.print_exc()
                return


if __name__ == '__main__':
    client = Client()
    if client.connect():
        client.send2server()


这个tostring把数据转成二进制 , 可以发送但是要还原的时候没有对应的方法 , 所以先把数据转为numpy格式,用他的方法可以还原数据


tcp服务端 :

有了之前tcp聊天室案例的基础 , 现在我们只要接收到从服务端发来的"视频数据"只要将它在逆操作得到可以原来的视频.

import socket
import struct
import threading
import cv2
import numpy
import pygame


class Server:
    def __init__(self):
        # 设置tcp服务端的socket
        self.server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        # 设置重复使用
        self.server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
        # 绑定地址和端口
        self.server.bind(('192.168.198.129', 11000))
        # 设置被动监听
        self.server.listen(128)

    def run(self):
        while True:
            print('等待客户端连接')
            # 等待客户端连接
            client, addr = self.server.accept()
            ProcessClient(client).start()


class ProcessClient(threading.Thread):

    def __init__(self, client):
        super().__init__()
        self.client = client
        # 创建一个窗口
        self.screen = pygame.display.set_mode((640, 480), 0, 32)

    def run(self):
        while True:
            data = self.client.recv(8)
            if not data:
                break
            # 图片的长度 图片的宽高
            length, width, height = struct.unpack('ihh', data)

            imgg = b''  # 存放最终的图片数据
            while length:
                # 接收图片
                temp_size = self.client.recv(length)
                length -= len(temp_size)  # 每次减去收到的数据大小
                imgg += temp_size  # 每次收到的数据存到img里

            # 把二进制数据还原
            data = numpy.fromstring(imgg, dtype='uint8')

            # 还原成矩阵数据
            image = cv2.imdecode(data, cv2.IMREAD_UNCHANGED)
            print(image)
          
            cv2.imshow('窗口1', image)
            cv2.waitKey(1)
            

if __name__ == '__main__':
    server = Server()
    server.run()

使用cv2.imshow显示视频,

因为地址和端口每个人不统一, 自己改一下 window系统使用cmd输出ipconfig查看自己的ip



优化:

之前用pygame做过 飞机大战 也可以利用pygame的窗口显示视频 , 只需要添加如下代码 :

			
            # 把bgr转rgb
            img = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
            # 转换为pygame能显示的数据格式
            img = pygame.image.frombuffer(img, (640, 480), 'RGB')
            # 显示 在pygame上
            self.screen.blit(img, (0, 0))
            pygame.display.update()

opencv的色彩模式是 :bgr
pygame的色彩模式是 :rgb

我们用cv2获取到的数据想要在pygame上显示清楚 , 就要用cvtColor转换一下

.

现在的程序流程是 : 当客户端有一个人发来数据时把这个服务器扔进线程去处理, 然后开启一个pygame窗口 , 当第二个客户端发来数据时 , 那还是会在创建一个pygame窗口 ,我们接下来想让这些数据显示在一个窗口里面 , 就像是微信多人视频那样…

服务器完整代码 :

import queue
import socket
import struct
import threading

import cv2
import numpy
import pygame


class Server:
    def __init__(self, q):
        # 设置tcp服务端的socket
        self.server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        # 设置重复使用
        self.server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
        # 绑定地址和端口
        self.server.bind(('192.168.52.1', 11000))
        # 设置被动监听
        self.server.listen(128)
        # 队列
        self.q = q
        # 定义一个字典 , 记录每个窗口是否显示内容
        self.nums = {1: False, 2: False, 3: False, 4: False, 5: False, 6: False}

    def waiting(self):
        while True:
            print('等待客户端连接')
            # 等待客户端连接
            client, addr = self.server.accept()

            for i, is_on in self.nums.items():
                # 遍历字典 从前往后 找到没开的窗口
                if not is_on:
                    # 找到了没用的窗口 , 开始使用 ,并改变他的值
                    self.nums[i] = True
                    # 把队列传入客户端的线程里 i是窗口号
                    ProcessClient(client, self.q, self.nums,i).start()
                    break


class ProcessClient(threading.Thread):
    client: socket.socket

    def __init__(self, client, q, nums,i):
        super().__init__()
        self.client = client
        self.picture = pygame.image.load("picture.png")
        self.q = q
        self.i = i
        self.nums = nums

    def run(self):
        while True:
            try:
                data = self.client.recv(8)
                # 如果接收不到数据
                if not data:
                    self.nums[self.i] = False
                    self.q.put([self.picture, self.i])
                    break
                # 图片的长度 图片的宽高
                length, width, height = struct.unpack('ihh', data)

                self.img = b''  # 存放最终的图片数据
                while length:
                    # 接收图片
                    temp_size = self.client.recv(length)
                    length -= len(temp_size)  # 每次减去收到的数据大小
                    self.img += temp_size  # 每次收到的数据存到img里

                # 把二进制数据还原
                data = numpy.fromstring(self.img, dtype='uint8')

                # 还原成矩阵数据
                image = cv2.imdecode(data, cv2.IMREAD_UNCHANGED)
                print(image)
                # opencv是bgr
                # pygame是rgb

                img = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)

                # 转换为pygame能显示数据格式
                img = pygame.image.frombuffer(img, (640, 480), 'RGB')

                self.q.put([img, self.i])
            except:
                self.nums[self.i] = False
                self.client.close()
                # 如果断开连接 显示一张默认图片
                self.q.put([self.picture, self.i])
                break


class MyPygame(threading.Thread):

    def __init__(self, q):
        super().__init__()
        self.q = q

    def run(self):

        pygame.init()
        self.screen = pygame.display.set_mode((640 * 3, 480 * 2), 0, 32)
        image = pygame.image.load("picture.png")
        self.screen.blit(image, (0, 0))

        while True:
            for event in pygame.event.get():

                if event.type == pygame.QUIT:
                    print("exit")
                    pygame.quit()
                    exit()

            try:
                # 从队列获取要显示的图片
                img, num = self.q.get(timeout=0.1)

                pass
            except:
                pass
            else:
                # 处理图片
                self.show_img(img, num)

            finally:
                pygame.display.update()

    def show_img(self, img, num):
        if num == 1:

            # 显示图片
            self.screen.blit(img, (640 * 0, 480 * 0))
        elif num == 2:
            self.screen.blit(img, (640 * 1, 480 * 0))
        elif num == 3:
            self.screen.blit(img, (640 * 2, 480 * 0))
        elif num == 4:
            self.screen.blit(img, (640 * 0, 480 * 1))
        elif num == 5:
            self.screen.blit(img, (640 * 1, 480 * 1))
        elif num == 6:
            self.screen.blit(img, (640 * 2, 480 * 1))


if __name__ == '__main__':
    # 队列
    q = queue.Queue()
    # 显示pygame的线程
    mygame = MyPygame(q).start()

    server = Server(q)
    server.waiting()

每个客户端发来的连接都在一个线程里 , 带上他的窗口号(分配好的) , put进 队列 里,再创建一个线程 用来显示这些收到的数据,
这是别人教我的一种思路…

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值