Python-OpenCV使用多线程实现依次以高分辨率读取摄像头

  • 1.配置环境
  • 2.任务要求
  • 3.技术难点
  • 4.技术流程
  • 5.代码实现
  • 6.结束语


1.配置环境

使用环境:python3.7
平台:Windows10
IDE:PyCharm

2.任务要求

按照一定频率读取灰点相机图像、雷达、GPS数据,并且保持数据同步。如以相机为例则可理解为以50HZ即50FPS读取图像

python中如何获取单像素 python用相机采集图像_POINT GRAY

3.技术难点

  • 如何保证三个设备读取的数据同步
  • 如何保证固定的频率

4.技术流程

读取摄像头需要安装灰点相机的Python包,若为安装的话参考博主前一篇博客

为了同步时间,采用的技术流程如下:

python中如何获取单像素 python用相机采集图像_多线程缓存_02

通过一个socket不断以一个固定评率,如50HZ向外发送采集数据的信号和时间戳得到稳定的信号源
对于图像则不断接收socket信号,当接收到采集信号时,则采集一张照片写入本地。

但是博主在储存图片的时候发现,电脑储存图片的极限速度只有10几个FPS,这样会导致整体的频率下降。所以博主才用了缓存的方法。方法如下:

python中如何获取单像素 python用相机采集图像_灰点相机_03


对采集数据和储存数据作优化,先将采集到的数据存入到缓存中,这样便可以保证图像可以以指定的频率获取,相较于最初的方法,最大的不同是,当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定时发生策略,产生稳定的时间帧
  • 采用多线程缓存技术,让时间得到同步