Python-OpenCV使用多线程实现依次以高分辨率读取摄像头
- 1.配置环境
- 2.任务要求
- 3.技术难点
- 4.技术流程
- 5.代码实现
- 6.结束语
1.配置环境
使用环境:python3.7
平台:Windows10
IDE:PyCharm
2.任务要求
按照一定频率读取灰点相机图像、雷达、GPS数据,并且保持数据同步。如以相机为例则可理解为以50HZ即50FPS读取图像
3.技术难点
- 如何保证三个设备读取的数据同步
- 如何保证固定的频率
4.技术流程
读取摄像头需要安装灰点相机的Python包,若为安装的话参考博主前一篇博客
为了同步时间,采用的技术流程如下:
通过一个socket不断以一个固定评率,如50HZ向外发送采集数据的信号和时间戳得到稳定的信号源
对于图像则不断接收socket信号,当接收到采集信号时,则采集一张照片写入本地。
但是博主在储存图片的时候发现,电脑储存图片的极限速度只有10几个FPS,这样会导致整体的频率下降。所以博主才用了缓存的方法。方法如下:
对采集数据和储存数据作优化,先将采集到的数据存入到缓存中,这样便可以保证图像可以以指定的频率获取,相较于最初的方法,最大的不同是,当UDP已经发送中止采集任务
后,图像抓取任务结束,但是从缓存中读取图像并储存的任务需要将缓存中图像存完才可结束。
5.代码实现
SaveToImg_color_v1.3.py
# author:Hurricane
# date: 2021/7/18
# E-mail:hurri_cane@qq.com
# 单线程存储图片
# 使用缓存来存储未被储存的图片
# 有一个问题:缓存到327张图片后就不会增加新的图片
import PySpin
import datetime
import time
import sys
from threading import Thread
import socket
import json
from tqdm import tqdm
class AviType:
"""'Enum' to select AVI video type to be created and saved"""
UNCOMPRESSED = 0
MJPG = 1
H264 = 2
class main(object):
def __init__(self, img_path):
self.img_path = img_path
self.cam = None
self.flag_get_img = False
self.flag_finish_collect = False
self.images = []
self.images_file = []
self.BUFSIZ = 1024 # 接收消息的缓冲大小
self.Local_ADDR = ('127.0.0.1', 12222)
self.udpSerSock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # 创建客户端套接字
self.udpSerSock.bind(self.Local_ADDR)
def system_init(self):
# Retrieve singleton reference to system object
self.system = PySpin.System.GetInstance()
# Get current library version
self.version = self.system.GetLibraryVersion()
print('Library version: %d.%d.%d.%d' % (
self.version.major, self.version.minor, self.version.type, self.version.build))
# Retrieve list of cameras from the system
self.cam_list = self.system.GetCameras()
self.num_cameras = self.cam_list.GetSize()
print('Number of cameras detected:', self.num_cameras)
# Finish if there are no cameras
if self.num_cameras == 0:
# Clear camera list before releasing system
self.cam_list.Clear()
# Release system instance
self.system.ReleaseInstance()
print('Not enough cameras!')
return False
def run_single_camera(self):
try:
self.cam = self.cam_list[0]
# Retrieve TL device nodemap and print device information
self.nodemap_tldevice = self.cam.GetTLDeviceNodeMap()
self.node_serial = PySpin.CStringPtr(self.nodemap_tldevice.GetNode('DeviceSerialNumber'))
self.device_serial_number = self.node_serial.GetValue()
self.print_device_info(self.nodemap_tldevice)
# Initialize camera
self.cam.Init()
# Retrieve GenICam nodemap
self.nodemap = self.cam.GetNodeMap()
# Acquire list of images
self.get_images_init()
self.get_images()
except PySpin.SpinnakerException as ex:
print('Error: %s' % ex)
def get_images_init(self):
print('*** IMAGE ACQUISITION ***\n')
try:
self.get_images_result = True
# Set acquisition mode to continuous
node_acquisition_mode = PySpin.CEnumerationPtr(self.nodemap.GetNode('AcquisitionMode'))
if not PySpin.IsAvailable(node_acquisition_mode) or not PySpin.IsWritable(node_acquisition_mode):
print('Unable to set acquisition mode to continuous (enum retrieval). Aborting...')
return False
# Retrieve entry node from enumeration node
node_acquisition_mode_continuous = node_acquisition_mode.GetEntryByName('Continuous')
if not PySpin.IsAvailable(node_acquisition_mode_continuous) or not PySpin.IsReadable(
node_acquisition_mode_continuous):
print('Unable to set acquisition mode to continuous (entry retrieval). Aborting...')
return False
acquisition_mode_continuous = node_acquisition_mode_continuous.GetValue()
node_acquisition_mode.SetIntValue(acquisition_mode_continuous)
print('Acquisition mode set to continuous...')
# Begin acquiring images
self.cam.BeginAcquisition()
print('Acquiring images...')
except PySpin.SpinnakerException as ex:
print('Error: %s' % ex)
self.get_images_result = False
def get_images(self):
try:
while True:
if not self.flag_finish_collect:
try:
# Retrieve next received image
self.image_result = self.cam.GetNextImage()
# Ensure image completion
if self.image_result.IsIncomplete():
print('Image incomplete with image status %d...' % self.image_result.GetImageStatus())
else:
if self.flag_get_img:
self.file_name = self.img_path + '\%s-%s.jpg' % (self.timestamp,
self.device_serial_number)
self.images.append(self.image_result)
self.images_file.append(self.file_name)
self.flag_get_img = False
except PySpin.SpinnakerException as ex:
print('Error: %s' % ex)
else:
break
except:
print("Error,can't get images!")
def save_img(self):
while True:
if len(self.images) > 0:
pass
assert len(self.images) == len(self.images_file),"ERROR:图片时间帧不匹配!"
self.images[0].Save(self.images_file[0])
print('Image saved at %s.jpg' % self.images_file[0],"| %d images left in buffer"%len(self.images_file))
del self.images[0], self.images_file[0]
else:
if self.flag_finish_collect:
self.clear_system()
def print_device_info(slef, nodemap):
print('\n*** DEVICE INFORMATION ***\n')
try:
result = True
node_device_information = PySpin.CCategoryPtr(nodemap.GetNode('DeviceInformation'))
if PySpin.IsAvailable(node_device_information) and PySpin.IsReadable(node_device_information):
features = node_device_information.GetFeatures()
for feature in features:
node_feature = PySpin.CValuePtr(feature)
print('%s: %s' % (node_feature.GetName(),
node_feature.ToString() if PySpin.IsReadable(
node_feature) else 'Node not readable'))
else:
print('Device control information not available.')
except PySpin.SpinnakerException as ex:
print('Error: %s' % ex)
return False
return result
def clear_system(self):
print("All done")
sys.exit(0)
def trigger(self):
# 开启子线程
# task_Proc_loop = Thread(target=self.show_img_len)
# # 设置为守护线程,当主线程结束后,此子线程也会随之结束
# task_Proc_loop.setDaemon(True)
# task_Proc_loop.start()
task_Proc_stop_single = Thread(target=self.receive_msg)
# 设置为守护线程,当主线程结束后,此子线程也会随之结束
task_Proc_stop_single.setDaemon(True)
task_Proc_stop_single.start()
task_Proc_save_img = Thread(target=self.save_img)
# 设置为守护线程,当主线程结束后,此子线程也会随之结束
task_Proc_save_img.setDaemon(True)
task_Proc_save_img.start()
def receive_msg(self):
while True:
data_org, addr = self.udpSerSock.recvfrom(1024)
message_type = data_org[0:1].decode('utf-8')
if message_type == "0":
data = json.loads(data_org[1:].decode('utf-8'))
# print(time.ctime(), data)
if data.__contains__("collection_data"):
self.flag_get_img = data['collection_data']
if data.__contains__("finish_collect"):
self.flag_finish_collect = data['finish_collect']
if data.__contains__("timestamp"):
self.timestamp = data['timestamp']
if __name__ == '__main__':
img_path = r"D:\Program\Datasets\Heading_angle\imgset1"
Process = main(img_path)
Process.system_init()
Process.trigger()
Process.run_single_camera()
client_for_collect_img.py
# author:Hurricane
# date: 2021/6/12
# E-mail:hurri_cane@qq.com
from socket import *
from time import ctime
import json
import time
import datetime
print("=====================时间戳UDP客户端=====================")
class client():
def __init__(self, FPS):
self.FPS = FPS
self.time_interval = 1 / self.FPS
def parameter_init(self):
self.BUFSIZ = 1024 # 接收消息的缓冲大小
self.Local_ADDR = ('127.0.0.1', 12000)
self.Remote_ADDR_1 = ('127.0.0.1', 12222)
self.udpCliSock = socket(AF_INET, SOCK_DGRAM) # 创建客户端套接字
self.udpCliSock.bind(self.Local_ADDR) # 套接字与地址绑定
def send_message(self):
self.time = time.time()
while True:
if time.time() - self.time > self.time_interval:
print(time.time() - self.time)
start_time = datetime.datetime.now().strftime('%Y-%m-%d-%Hh%Mm%Ss%fus')
data = {"collection_data": True, "timestamp": start_time}
# data = {"collection_data": False, }
# data = {"finish_collect": True,}
data = json.dumps(data)
data = b"0" + bytes(data, 'utf-8')
self.udpCliSock.sendto(data, self.Remote_ADDR_1)
self.time = time.time()
print("message have sent!",data)
if __name__ == '__main__':
FPS = 50
client_ = client(FPS)
client_.parameter_init()
client_.send_message()
使用方法:
先运行SaveToImg_color_v1.3.py
,再运行client_for_collect_img.py
便会开始采集图像数据
主要技术要点如下:
- 采用socket定时发生策略,产生稳定的时间帧
- 采用多线程缓存技术,让时间得到同步