python 基于http方式与基于redis方式传输摄像头图片数据的实现和对比
目录
- 0. 需求
- 1. 基于http方式传递图片数据
- 1.1 发送图片数据
- 1.2 接收图片数据并可视化
- 1.3 测试
- 2. 基于redis方式传递图片数据
- 2.1 发送图片数据
- 2.2 接收图片数据并可视化
- 2.3 测试
- 3. 对比
0. 需求
在不同进程或者不同语言间传递摄像头图片数据,比如从java实现的代码中获取摄像头画面数据,将其传递给python实现的算法代码中进行处理。这里,提供基于http方式和基于redis方式这两种方式进行实现,并比较两者传输速度。
作为样例,代码均采用python实现,运行环境为ubuntu 18.04。
1. 基于http方式传递图片数据
1.1 发送图片数据
-
思路:创建两个线程,一个线程利用Opencv通过rtsp地址获得摄像头画面,一个线程将摄像头图片数据转为字节流,并通过http方式发送。
-
实现
#coding=gb2312
# 文件名:http_send.py
import requests
import base64
import cv2
import time
import threading
from queue import LifoQueueclass 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 = Falseself.cap.release()if __name__ == '__main__':rtsp_read = rtspRead("rtsp://xx:xx@xx", 9322) rtsp_read.start()
1.2 接收图片数据并可视化
-
思路:通过flask框架接收http请求,并将接收到的图片数据的字节流转为np格式,并进一步转为opencv格式。另起一个线程,接收opencv格式的图片数据,并做显示。
-
实现
#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 timeclass 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 = Falseif self.frame is not None:print("展示frame")cv2.imshow('http_pic', self.frame)if cv2.waitKey(1) & 0xFF == ord('q'):breakcv2.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 测试
# 先开一个terminal窗口,启用接收进程
python http_receive.py# 再开一个terminal窗口,启用发送进程
python http_send.py
2. 基于redis方式传递图片数据
2.1 发送图片数据
-
思路:创建两个线程,一个线程利用Opencv通过rtsp地址获得摄像头画面,一个线程将摄像头图片数据转为字节流,并通过redis方式发送。这里的redis方式具体指的是,redis是一个内存数据库,通过键值对存储数据,通过订阅/发布机制传递消息,所以将图片字节流数据存入redis中,并将存入消息发布出去,实现发送效果。
-
实现
#coding=gb2312
# 文件名:redis_send.py
import redis
import cv2
import time
import base64
import threading
from queue import LifoQueueclass 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 = cameraipdef 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 resultdef delete(self):self.r.delete('result')self.r.delete(self.cameraip)class rtspRead: # rtsp地址读取def __init__(self, rtsp, cameraip):self.rtsp = rtspself.cameraip = cameraipself.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 = 0while 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 breakif 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 breakif 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 += 1if num > 100:print("发送收发100次,平均耗时{}s,平均速度为{}帧/秒".format(total_time/100, round(100/total_time,2)))num = 0total_time = 0self.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 = Falseself.cap.release()if __name__ == '__main__':rtsp_read = rtspRead("rtsp://xx:xx@xx", 'xx') rtsp_read.start()
2.2 接收图片数据并可视化
-
思路:通过redis数据库的消息监听机制,当接收到数据入库消息,则提取图片字节流数据,并将其处理为opencv格式的图片数据,从而做到显示。
-
实现
#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 npr = redis.Redis(host='127.0.0.1',port=6379,db=0)
ps = r.pubsub()
charecter = "img"
ps.subscribe(charecter + str(0))is_first = Truefor 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 测试
# 先开一个terminal窗口,启用接收进程
python redis_receive.py# 再开一个terminal窗口,启用发送进程
python redis_send.py
结束进程直接在显示窗口上按下q键即可。
3. 对比
综合来看,在可视化摄像头画面的前提下,两者均可做到实时显示。其中,采用redis方式速度为14帧/秒左右,采用http方式速度为10帧/秒左右。
若要提高速度,可取消base64的加密过程;若仅考虑传输,可取消其中的可视化部分,传输速度应该会进一步提高。
相关文章:
python 基于http方式与基于redis方式传输摄像头图片数据的实现和对比
目录 0. 需求1. 基于http方式传递图片数据1.1 发送图片数据1.2 接收图片数据并可视化1.3 测试 2. 基于redis方式传递图片数据2.1 发送图片数据2.2 接收图片数据并可视化2.3 测试 3. 对比 0. 需求 在不同进程或者不同语言间传递摄像头图片数据,比如从java实现的代码…...
快速使用Git完整开发
本系列有两篇文章: 一是本篇,主要说明了关于Git工具的基础使用,包含三板斧(git add、git commit、git push)、Git基本配置、版本回退、分支管理、公钥与私钥、远端仓库和远端分支、忽略文件、命令别名、标签等内容。二…...
鲁棒优化入门(7)—Matlab+Yalmip两阶段鲁棒优化通用编程指南(下)
0.引言 上一篇博客介绍了使用Yalmip工具箱求解单阶段鲁棒优化的方法。这篇文章将和大家一起继续研究如何使用Yalmip工具箱求解两阶段鲁棒优化(默认看到这篇博客时已经有一定的基础了,如果没有可以看看我专栏里的其他文章)。关于两阶段鲁棒优化与列与约束生成算法的原…...
Docker技术--Docker中的网络问题
1.docker中的网络通信 如果想要弄清楚docker中的网络通信问题,其实需要弄清楚这几个问题就可以:容器与容器之间的通信、容器与外部网络之间的通信、外部网络与容器之间的通信。 -a:容器与容器之间的通信,如下所示: 在默认情况下,docker使用网桥(Bridge模式)与NAT通信。这…...
ASP.NET Core 中的两种 Web API
ASP.NET Core 有两种创建 RESTful Web API 的方式: 基于 Controller,使用完整的基于ControllerBase的基类定义接口endpoints。基于 Minimal APIs,使用Lambda表达式定义接口 endpoints。 基于 Controller 的 Web API 可以使用构造函数注入&a…...
【线程池】如何判断线程池中的任务执行完毕(三)
目录 前言 1. isTerminated()方法 2. awaitTermination()方法 3.getTaskCount()方法和executor.getCompletedTaskCount()方法结合使用 4.使用CountDownlatch类 前言 通常我们使用线程池的时候,系统处于运行的状态,而线程池本身就是主要为了线程复用&…...
Qt/C++编写视频监控系统81-Onvif报警抓图和录像并回放
一、前言 视频监控系统中的图文警情模块,是通过Onvif协议的事件订阅拿到的,通过事件订阅后,设备的各种报警事件比如入侵报警/遮挡报警/越界报警/开关量报警等,触发后都会主动往订阅者发送,而且一般都是会发送两次&…...
浅谈安防视频监控平台EasyCVR视频汇聚平台对于夏季可视化智能溺水安全告警平台的重要性
每年夏天都是溺水事故高发的时期,许多未成年人喜欢在有水源的地方嬉戏,这导致了悲剧的发生。常见的溺水事故发生地包括水库、水坑、池塘、河流、溪边和海边等场所。 为了加强溺水风险的提示和预警,完善各类安全防护设施,并及时发现…...
基于单片机的串行通信发射机设计
一、项目介绍 串行通信是一种常见的数据传输方式,允许将数据以比特流的形式在发送端和接收端之间传输。当前实现基于STC89C52单片机的串行通信发射机,通过红外发射管和接收头实现自定义协议的数据无线传输。 二、系统设计 2.1 单片机选择 在本设计中&…...
MySQL数据库——多表查询(3)-自连接、联合查询、子查询
目录 自连接 查询语法 自连接演示 联合查询 查询语法 子查询 介绍 标量子查询 列子查询 行子查询 表子查询 自连接 通过前面的学习,我们对于连接已经有了一定的理解。而自连接,通俗地去理解就是自己连接自己,即一张表查询多次。…...
day53 动规.p14 子序列
- 1143.最长公共子序列 cpp class Solution { public: int longestCommonSubsequence(string text1, string text2) { vector<vector<int>> dp(text1.size() 1, vector<int>(text2.size() 1, 0)); for (int i 1; i < text1.size(…...
将docker打包成镜像并保存到本地
如果想重装系统,又不想破坏docker里面配好的环境,那么可以将docker镜像打包到本地进行保存。 1. 将docker打包成镜像 命令:docker commit 容器id 镜像名:tag 使用docker ps -a即可查看容器相关信息 docker commit dd25c7c6bf17 zm_cu101:c…...
Harmony数据存储工具类
使用的是mmkv 1、安装mmkv ohpm install @ohos/mmkv2、封装 import{MMKV, SerializeBase} from @ohos/mmkv/*** 数据存储工具类*/ class MMKVUtil{private filePath:string = private cachePath:string = private mmkv:MMKVprivate mmapID:string="MMKV"construct…...
ROS 2官方文档(基于humble版本)学习笔记(一)
ROS 2官方文档(基于humble版本)学习笔记(一) 一、安装ROS 2二、按教程学习1.CLI 工具配置环境使用turtlesim,ros2和rqt安装 turtlesim启动 turtlesim使用 turtlesim安装 rqt使用 rqt重映射关闭turtlesim 由于市面上专门…...
【数据结构】十字链表的画法
十字链表的基本概念 有向边又称为弧 假设顶点 v 指向 w,那么 w 称为弧头,v 称为弧尾 顶点节点采用顺序存储 顶点节点 data:存放顶点的信息firstin:指向以该节点为终点(弧头)的弧节点firstout࿱…...
使用rabbitmq进行支付之后的消息通知
订单服务完成支付后将支付结果发给每一个与订单服务对接的微服务,订单服务将消息发给交换机,由交换机广播消息,每个订阅消息的微服务都可以接收到支付结果. 微服务收到支付结果根据订单的类型去更新自己的业务数据。 相关技术方案 使用消息…...
【100天精通python】Day47:python网络编程_Web开发:web服务器,前端基础以及静态服务器
目录 1 网络编程与web编程 1.1 网络编程 1.2 web编程 1.3 前后端交互的基本原理 2 Web开发基础 2.1 HTTP协议 2.2 Web服务器 2.3 前端基础 2.3.1 HTML(超文本标记语言) 2. 3.2 CSS(层叠样式表) 2.3.3 JavaScript 2.…...
Web框架Beego
beego简介第一个beego项目beego项目结构分析bee 工具简介beego参数配置beego路由设置beego控制器介绍beego获取参数beego ORMbeego orm高级查询beego 原生sql查询beego 模板语法指南beego模板处理...
Kubernetes(K8s)基本环境部署
此处只做学习使用,配置单master环境。 一、环境准备 1、ip主机规划(准备五台新机)>修改各个节点的主机名 注意:关闭防火墙与selinux 节点主机名ip身份joshua1 kubernetes-master.openlab.cn 192.168.134.151masterjoshua2k…...
antd5:form组件底层封装库field-form-1.37.0启动
一开始node版本是18.16.0 npm install发现安装依赖成功 npm start发现启动出错 node:internal/crypto/hash:71this[kHandle] new _Hash(algorithm, xofLen);^Error: error:0308010C:digital envelope routines::unsupportedat new Hash (node:internal/crypto/hash:71:19)…...
屋顶变身“发电站” ,中天合创屋面分布式光伏发电项目顺利并网!
5月28日,中天合创屋面分布式光伏发电项目顺利并网发电,该项目位于内蒙古自治区鄂尔多斯市乌审旗,项目利用中天合创聚乙烯、聚丙烯仓库屋面作为场地建设光伏电站,总装机容量为9.96MWp。 项目投运后,每年可节约标煤3670…...
保姆级教程:在无网络无显卡的Windows电脑的vscode本地部署deepseek
文章目录 1 前言2 部署流程2.1 准备工作2.2 Ollama2.2.1 使用有网络的电脑下载Ollama2.2.2 安装Ollama(有网络的电脑)2.2.3 安装Ollama(无网络的电脑)2.2.4 安装验证2.2.5 修改大模型安装位置2.2.6 下载Deepseek模型 2.3 将deepse…...
Mysql中select查询语句的执行过程
目录 1、介绍 1.1、组件介绍 1.2、Sql执行顺序 2、执行流程 2.1. 连接与认证 2.2. 查询缓存 2.3. 语法解析(Parser) 2.4、执行sql 1. 预处理(Preprocessor) 2. 查询优化器(Optimizer) 3. 执行器…...
uniapp 开发ios, xcode 提交app store connect 和 testflight内测
uniapp 中配置 配置manifest 文档:manifest.json 应用配置 | uni-app官网 hbuilderx中本地打包 下载IOS最新SDK 开发环境 | uni小程序SDK hbulderx 版本号:4.66 对应的sdk版本 4.66 两者必须一致 本地打包的资源导入到SDK 导入资源 | uni小程序SDK …...
基于PHP的连锁酒店管理系统
有需要请加文章底部Q哦 可远程调试 基于PHP的连锁酒店管理系统 一 介绍 连锁酒店管理系统基于原生PHP开发,数据库mysql,前端bootstrap。系统角色分为用户和管理员。 技术栈 phpmysqlbootstrapphpstudyvscode 二 功能 用户 1 注册/登录/注销 2 个人中…...
路由基础-路由表
本篇将会向读者介绍路由的基本概念。 前言 在一个典型的数据通信网络中,往往存在多个不同的IP网段,数据在不同的IP网段之间交互是需要借助三层设备的,这些设备具备路由能力,能够实现数据的跨网段转发。 路由是数据通信网络中最基…...
python打卡第47天
昨天代码中注意力热图的部分顺移至今天 知识点回顾: 热力图 作业:对比不同卷积层热图可视化的结果 def visualize_attention_map(model, test_loader, device, class_names, num_samples3):"""可视化模型的注意力热力图,展示模…...
手动给中文分词和 直接用神经网络RNN做有什么区别
手动分词和基于神经网络(如 RNN)的自动分词在原理、实现方式和效果上有显著差异,以下是核心对比: 1. 实现原理对比 对比维度手动分词(规则 / 词典驱动)神经网络 RNN 分词(数据驱动)…...
简单聊下阿里云DNS劫持事件
阿里云域名被DNS劫持事件 事件总结 根据ICANN规则,域名注册商(Verisign)认定aliyuncs.com域名下的部分网站被用于非法活动(如传播恶意软件);顶级域名DNS服务器将aliyuncs.com域名的DNS记录统一解析到shado…...
解密鸿蒙系统的隐私护城河:从权限动态管控到生物数据加密的全链路防护
摘要 本文以健康管理应用为例,展示鸿蒙系统如何通过细粒度权限控制、动态权限授予、数据隔离和加密存储四大核心机制,实现复杂场景下的用户隐私保护。我们将通过完整的权限请求流程和敏感数据处理代码,演示鸿蒙系统如何平衡功能需求与隐私安…...
