Implementación y comparación de la transmisión de datos de imágenes de cámara basada en Python basada en HTTP y Redis

0. Demanda

Pase datos de imágenes de la cámara entre diferentes procesos o diferentes lenguajes, como obtener datos de imágenes de la cámara del código implementado en Java y pasarlos al código del algoritmo implementado en Python para su procesamiento. Aquí, se proporcionan dos métodos basados ​​​​en http y redis para su implementación y se comparan las velocidades de transmisión de los dos.

Como ejemplo, el código está implementado en Python y el entorno operativo es Ubuntu 18.04.

1. Transfiera datos de imagen según el método http

1.1 Enviar datos de imagen
  • Idea: cree dos subprocesos, un subproceso usa Opencv para obtener la imagen de la cámara a través de la dirección rtsp y el otro subproceso convierte los datos de la imagen de la cámara en un flujo de bytes y los envía a través de http.

  • lograr

#coding=gb2312
# 文件名:http_send.py
import requests
import base64
import cv2
import time
import threading
from queue import LifoQueue


class rtspRead: # rtsp地址读取
    def __init__(self, rtsp, port):
        self.rtsp = rtsp # 摄像头的rtsp地址
        self.addr = "http://127.0.0.1:{}/image_post".format(port) # 本地http传输地址
        self.frameQueue = LifoQueue() # 视频帧的队列
        self.frameLock = threading.Lock() # 视频帧队列的锁
        
        self.threadFlag = True # 
        
    
    def start(self): # 开始
        t1 = threading.Thread(target=self.sendFrame, args=(), daemon=True)
        t2 = threading.Thread(target=self.readFrame, args=(), daemon=True)
        t1.start()
        t2.start()
        t1.join()
        t2.join()
        
    def sendFrame(self): # 通过http发送图片
        
        num = 0 # 计算100次图片发送到接受的平均时间,以及平均帧数

        while self.threadFlag:
            time.sleep(0.01)
        
            is_get_frame = False # 没有从队列中获得图片
            self.frameLock.acquire()
            if self.frameQueue.qsize():
                frame = self.frameQueue.get()
                is_get_frame = True # 从队列中获得图片
            self.frameLock.release()
            
            if is_get_frame:
            	# frame 是ndarray对象,这里是把原始ndarray转成jpg的字节流,转成其它格式直接替换jpg即可
                img_str = cv2.imencode('.jpg', frame)[1].tobytes()
                #使用b64encode对bytes-like类型对象进行编码(加密)并返回bytes对象
                img_data = base64.b64encode(img_str)
                data = {
    
    'img': img_data}
                
                resp = requests.post(self.addr, data=data) # 发送图片数据,并获得http_receive.py的返回信息
                print("结果:", resp.text)
            
        
    def readFrame(self): # 通过rtsp读取图片
        self.cap = cv2.VideoCapture(self.rtsp)
        
        if self.cap.isOpened():
            time.sleep(0.01)
        
            print("成功获得句柄!")
            while self.threadFlag:
                ret, frame = self.cap.read()
                if ret:
                      self.frameLock.acquire()
                      while self.frameQueue.qsize() > 3: # 尽量确保队列中为最新的图片帧
                          self.frameQueue.get()
                      self.frameQueue.put(frame)
                      self.frameLock.release()             
            
        else:
            print("句柄获得失败!")
            self.threadFlag = False
            
        self.cap.release()


if __name__ == '__main__':
    rtsp_read = rtspRead("rtsp://xx:xx@xx", 9322)    
    rtsp_read.start()
1.2 Recibir datos de imagen y visualizarlos
  • Idea: recibir solicitudes http a través del marco del matraz, convertir el flujo de bytes de los datos de la imagen recibidos al formato np y luego convertirlo al formato opencv. Inicie otro hilo para recibir los datos de la imagen en formato opencv y mostrarlos.

  • lograr

#coding=gb2312
# 文件名:http_receive.py
from flask import Flask, request
import base64
import numpy as np
import cv2
import threading
from queue import LifoQueue
import time

class RtspPlay():
    def __init__(self):
        self.frame = None # 视频帧
        self.threadFlag = True # 
        
    def start(self): # 开始
        t1 = threading.Thread(target=self.play, args=(), daemon=True)
        t1.start()
        #t1.join()
              
    def play(self):
        starttime = time.time()
        
        while self.threadFlag:
            time.sleep(0.01)
            
            print("进入展示线程")
            
            if time.time() - starttime > 260:
                self.threadFlag = False
        
            if self.frame is not None:
                print("展示frame")
                cv2.imshow('http_pic', self.frame)
                if cv2.waitKey(1) & 0xFF == ord('q'):
                    break
                    
        cv2.destroyAllWindows()
                    
    def setFrame(self, img):
        print("更新frame")
        self.frame = img.copy()
            
            
rtspPlay = RtspPlay()       
rtspPlay.start()


app = Flask(__name__)
@app.route('/image_post', methods=['POST'])
def img_post():
    if request.method == 'POST':
        # 获取图片数据
        img_base64 = request.form.get('img')
        # 把图片的二进制进行转化
        img_data = base64.b64decode(img_base64) #将拿到的base64的图片转换回来
        img_array = np.fromstring(img_data,np.uint8) # 转换np序列
        img = cv2.imdecode(img_array,cv2.COLOR_BGR2RGB)  # 转换Opencv格式
        mat = cv2.resize(img, (600, 600))
        print(mat.shape)
        
        rtspPlay.setFrame(mat)
        time.sleep(0.01)
  
    return 'receive img sucess'

if __name__ == "__main__":
    app.run(host="127.0.0.1", port=9322)


1.3 Pruebas
# 先开一个terminal窗口,启用接收进程
python http_receive.py

# 再开一个terminal窗口,启用发送进程
python http_send.py

2. Transfiera datos de imagen según el método redis

2.1 Enviar datos de imagen
  • Idea: cree dos subprocesos, un subproceso usa Opencv para obtener la imagen de la cámara a través de la dirección rtsp y el otro subproceso convierte los datos de la imagen de la cámara en un flujo de bytes y los envía a través de redis. El método redis aquí se refiere específicamente a que redis es una base de datos en memoria que almacena datos a través de pares clave-valor y entrega mensajes a través de un mecanismo de suscripción/publicación. Por lo tanto, los datos del flujo de bytes de la imagen se almacenan en redis y los mensajes almacenados se publican. Lograr efecto de envío.

  • lograr

#coding=gb2312
# 文件名:redis_send.py
import redis
import cv2
import time
import base64
import threading
from queue import LifoQueue
        
class redisSendPic: # redis发布者
    def __init__(self, cameraip):
        print("redis init ...")
        self.r = redis.Redis(host='127.0.0.1', port=6379,db=0) # 建立连接
        self.topic = 'img0' # 订阅的主题
        self.cameraip = cameraip
        
    def send(self, img):
        #print("发送图片")
        img_str = cv2.imencode('.jpg', img)[1].tobytes()
        data = base64.b64encode(img_str)
        self.r.set(self.cameraip, data)
        self.r.publish(self.topic, self.cameraip)
        
    def getResult(self,): 
        result = self.r.get('result')        
        return result
        
    def delete(self):
        self.r.delete('result')
        self.r.delete(self.cameraip)
            
class rtspRead: # rtsp地址读取
    def __init__(self, rtsp, cameraip):
        self.rtsp = rtsp
        self.cameraip = cameraip
        self.frameQueue = LifoQueue() # 视频帧的队列
        self.frameLock = threading.Lock() # 视频帧队列的锁
        
        self.threadFlag = True # 
        
    
    def start(self): # 开始
        t1 = threading.Thread(target=self.sendFrame, args=(), daemon=True)
        t2 = threading.Thread(target=self.readFrame, args=(), daemon=True)
        t1.start()
        t2.start()
        t1.join()
        t2.join()

        
        
    def sendFrame(self): # 通过redis发送图片
        self.sendpic = redisSendPic(self.cameraip)
        self.sendpic.r.set('result', 'start')
        
        num = 0 # 计算100次图片发送到接受的平均时间,以及平均帧数
        total_time = 0
    
        while self.threadFlag:
            time.sleep(0.01)
        
            is_get_frame = False # 没有从队列中获得图片
            self.frameLock.acquire()
            if self.frameQueue.qsize():
                frame = self.frameQueue.get()
                is_get_frame = True # 从队列中获得图片
            self.frameLock.release()
            
            result = self.sendpic.getResult() # 从接受进程获得是否中止的信号
            if result is not None and str(result, 'utf-8') == 'stop':
                self.threadFlag = False 
                break
            
            if is_get_frame:
                self.sendpic.r.delete('receive_time') # 删除接受时间
                send_time = time.time() # 发送时间
                self.sendpic.send(frame) # 发送图片
                
                receive_time = self.sendpic.r.get('receive_time') # 获得接受图片时间
                while receive_time is None: # 等待接受图片
                    #print("等待接受")
                    time.sleep(0.01)
                    receive_time = self.sendpic.r.get('receive_time')
                    
                    result = self.sendpic.getResult() # 从接受进程获得是否中止的信号
                    if result is not None and str(result, 'utf-8') == 'stop':
                        self.threadFlag = False 
                        break
                
                if receive_time is not None:
                    total_time += float(str(receive_time, 'utf-8')) - send_time
                #print("图像发送到接受时间:", float(str(receive_time, 'utf-8')) - send_time)
                
                num += 1
                if num > 100:
                    print("发送收发100次,平均耗时{}s,平均速度为{}帧/秒".format(total_time/100, round(100/total_time,2)))
                    num = 0
                    total_time = 0
        
        self.sendpic.delete()
            
        
    def readFrame(self): # 通过rtsp读取图片
        self.cap = cv2.VideoCapture(self.rtsp)
        
        if self.cap.isOpened():
            time.sleep(0.01)
        
            print("成功获得句柄!")
            while self.threadFlag:
                ret, frame = self.cap.read()
                if ret:
                      self.frameLock.acquire()
                      while self.frameQueue.qsize() > 3: # 尽量确保队列中为最新的图片帧
                          self.frameQueue.get()
                      self.frameQueue.put(frame)
                      self.frameLock.release()             
            
        else:
            print("句柄获得失败!")
            self.threadFlag = False
            
        self.cap.release()
        
        
if __name__ == '__main__':
    rtsp_read = rtspRead("rtsp://xx:xx@xx", 'xx')    
    rtsp_read.start()





2.2 Recibir datos de imagen y visualizarlos
  • Idea: a través del mecanismo de escucha de mensajes de la base de datos redis, cuando se recibe un mensaje de almacenamiento de datos, los datos del flujo de bytes de la imagen se extraen y procesan en datos de imagen en formato opencv para su visualización.

  • lograr

#coding=utf-8
# 文件名:redis_receive.py
import redis
import time
import scipy.misc
import cv2
import base64
from PIL import Image
import io
import time
import scipy.misc
import numpy as np

r = redis.Redis(host='127.0.0.1',port=6379,db=0)
ps = r.pubsub()
charecter = "img"
ps.subscribe(charecter + str(0))

is_first = True

for item in ps.listen():
    print("get message, ", item)
    #r.set("result", str("ok"))
    
    
    if is_first:
        r.set('receive_time', str(time.time())) # 获得可处理图片时间
        is_first = False 
    
    start = time.time()
    if item['type'] == 'message' and item['data'] is not None:
        img_base64 = r.get(str(item['data'], 'utf-8'))
        img_data = base64.b64decode(img_base64) #将拿到的base64的图片转换回来
        img_array = np.fromstring(img_data,np.uint8) # 转换np序列
        img = cv2.imdecode(img_array,cv2.COLOR_BGR2RGB)  # 转换Opencv格式
        r.set('receive_time', str(time.time())) # 获得可处理图片时间
        mat = cv2.resize(img, (600, 600))
        print(mat.shape)
        cv2.imshow('redis_pic', mat)
        if cv2.waitKey(1) & 0xFF == ord('q'):
            break
        #scipy.misc.imsave('D:/video.png', img)
        # frame = cv2.resize(img_data, (0, 0), fx=0.5, fy=0.5)
        #r.delete(charecter + str(0))
        #r.set("result", str("ok"))
        print("cost time:", time.time() - start)
        
cv2.destroyAllWindows()
r.set("result", str("stop"))        

2.3 Pruebas
# 先开一个terminal窗口,启用接收进程
python redis_receive.py

# 再开一个terminal窗口,启用发送进程
python redis_send.py

Para finalizar el proceso, simplemente presione la tecla q en la ventana de visualización.

3. Comparación

En general, bajo la premisa de visualizar la pantalla de la cámara, ambas se pueden visualizar en tiempo real. Entre ellos, la velocidad del método redis es de aproximadamente 14 cuadros por segundo y la velocidad del método http es de aproximadamente 10 cuadros por segundo.

Si desea aumentar la velocidad, puede cancelar el proceso de cifrado base64; si solo considera la transmisión, puede cancelar la parte de visualización y la velocidad de transmisión debería mejorarse aún más.

Guess you like

Origin blog.csdn.net/qq_30841655/article/details/132618369