Frame skipping while recording frames observed intermittently
Hi I have a basler machine vision camera acA2040-120uc.
Problem: While recording frames at high speed (100 fps) frame skip issue has been observed. in few videos (1 out of 5 approx).
Hardware setup: basler machine vision camera acA2040-120uc connected to usb3.0 slot directly to system. System also has arduino micro controller connected to another usb slot. All software in python language using pypylon api. USBFS memory increased to 1000 mb. OS is ubuntu 18.04
Code file attached:
[MVRecordingModule_v5.txt](https://github.com/basler/pypylon/files/7311272/MVRecordingModule_v5.txt)
Dont know if it because of coding of hardware setup/os Regards Girish
import os
import xml.etree.ElementTree as ET
import logging
import cv2
import numpy as np
import datetime
import time
from pypylon import pylon
import serial
import queue
import traceback
import threading
from logging import handlers
image_Dict={}
img_number=-1
class SGNImageEventHandler(pylon.ImageEventHandler):
def OnImageGrabbed(self, camera, grabResult):
global image_Dict,img_number
if grabResult.GrabSucceeded() and int(grabResult.ImageNumber)!=img_number:
img_number=int(grabResult.ImageNumber)
image_Dict[img_number]=grabResult.Array.copy()
#print(img_number)
class MVRecordingModule:
global image_Dict,img_number
def __init__(self,mv_recording_config_fl):
try:
self.configRoot=ET.parse(mv_recording_config_fl).getroot()
#LOGGING parameters
self.mv_cam_log_file = self.configRoot[0][0].text
self.log_level = int(self.configRoot[0][1].text)
self.logger=logging.getLogger("MV_RECORDING")
self.logger.setLevel(self.log_level)
logFormat=logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
log_fh=handlers.RotatingFileHandler(self.mv_cam_log_file,maxBytes=1048576,backupCount=5)
log_fh.setLevel(self.log_level)
log_fh.setFormatter(logFormat)
self.logger.addHandler(log_fh)
self.logger.debug("MV Recording Module initilized")
#Video recording initilizations
self.TMP_VID_REC_LOC=self.configRoot[1][0].text
self.SAVE_VID_LOC = self.configRoot[1][1].text
self.FILESAVEPREFIX = self.configRoot[1][2].text
dt = datetime.datetime.now().strftime('%Y-%m-%d_%H-%M-%S')
self.video_fileName = self.FILESAVEPREFIX+"_"+dt+".avi"
self.recording_cutOff=int(self.configRoot[1][3].text)
self.RECORDING_FPS = int(self.configRoot[1][4].text)
self.record_start_time=-1
#MV Camera settings
self.CAM_EXPOSURE=float(self.configRoot[2][0].text)
self.CAM_FPS=int(self.configRoot[2][1].text)
self.init_CAM()
self.MV_Q={}
self.img_number=-1
#TRIGGER settings
self.TRIGGER_FILE_PATH = self.configRoot[3][0].text
self.TRIGGER_FILE_NAME = self.configRoot[3][1].text
try:
self.ser = serial.Serial('/dev/ttyACM0',9600,timeout = 1)
time.sleep(3)
self.ser.write(b'0')
except:
self.ser = serial.Serial('/dev/ttyACM1',9600,timeout = 1)
time.sleep(3)
self.ser.write(b'0')
self.ARDUINO_STATE="NT"
self.grab_state=False
self.img_number_logger="IMG_NUMber.txt"
except Exception as e:
self.logger.critical("Error in MvrecordingModule Initilization")
self.logger.critical(str(e))
self.logger.critical(str(traceback.format_exc()))
def init_CAM(self):
try:
self.camera=pylon.InstantCamera(pylon.TlFactory.GetInstance().CreateFirstDevice())
self.camera.Open()
#self.camera.SensorReadoutMode="Fast"
self.camera.AcquisitionFrameRateEnable=True
self.camera.AcquisitionFrameRate=self.CAM_FPS
self.camera.ExposureTime=self.CAM_EXPOSURE
self.camera.PixelFormat="BayerRG8"
except Exception as e:
self.logger.critical("Error in init_CAM ")
self.logger.critical(str(e))
self.logger.critical(str(traceback.format_exc()))
def grab_CAM(self):
try:
self.logger.debug("Grab Started")
self.record_start_time=int(time.time())
self.grab_state=True
self.camera.RegisterImageEventHandler(SGNImageEventHandler(),pylon.RegistrationMode_Append, pylon.Cleanup_Delete)
self.camera.StartGrabbing(pylon.GrabStrategy_OneByOne, pylon.GrabLoop_ProvidedByInstantCamera)
while self.grab_state:
try:
if os.path.exists(os.path.join(self.TRIGGER_FILE_PATH,self.TRIGGER_FILE_NAME)) and (abs(int(time.time())-self.record_start_time)<self.recording_cutOff):
time.sleep(10)
else:
self.grab_state=False
self.logger.debug("Grabbing stopped, trigger removed or timeout")
time.sleep(1)
break
except Exception as e:
self.logger.debug("Error in machine vision camera grab:")
self.logger.debug(str(e))
self.logger.debug(str(traceback.format_exc()))
continue
self.camera.Close()
self.logger.debug("Grab Ended")
self.grab_state=False
except Exception as e:
self.logger.critical("Error in grab_CAM ")
self.logger.critical(str(e))
self.logger.critical(str(traceback.format_exc()))
def proc_q(self):
try:
while img_number==-1:
pass
self.logger.debug("Video rec Started")
flimgnumber=open(self.img_number_logger,"a+")
frame_ctr=0
imageList=list(image_Dict.keys())
imageList.sort()
img=cv2.cvtColor(image_Dict.get(imageList[0]),cv2.COLOR_BayerBG2RGB) #use with BayerBG format
frame_width=img.shape[1]
frame_height=img.shape[0]
fourcc=cv2.VideoWriter_fourcc('H','2','6','4')
fps=self.RECORDING_FPS
dt = datetime.datetime.now().strftime('%Y-%m-%d_%H-%M-%S')
self.video_fileName = self.FILESAVEPREFIX+"_"+dt+".avi"
flimgnumber.write(str(self.video_fileName))
flimgnumber.write("\n")
video_flName=os.path.join(self.TMP_VID_REC_LOC,self.video_fileName)
video_writer=cv2.VideoWriter(video_flName,fourcc,fps,(frame_width,frame_height))
video_writer.write(img)
frame_ctr+=1
while len(list(image_Dict.keys()))>0 or self.grab_state:
try:
if len(list(image_Dict.keys()))>0:
img_kys=list(image_Dict.keys())
img_kys.sort()
img_tmp_ky=img_kys[0]
flimgnumber.write(str(img_tmp_ky)+",")
img=cv2.cvtColor(image_Dict.get(img_tmp_ky),cv2.COLOR_BayerRG2RGB) # use with Bayer format
video_writer.write(img)
flimgnumber.flush()
frame_ctr+=1
image_Dict.pop(img_tmp_ky)
if self.grab_state==True:
time.sleep(0.001)
else:
time.sleep(0.001)
continue
except Exception as e:
self.logger.debug("Error inside proc_q- video rec loop:")
self.logger.debug(str(e))
self.logger.debug(str(traceback.format_exc()))
continue
flimgnumber.write("\nEnd of video")
flimgnumber.write("\n")
flimgnumber.flush()
flimgnumber.close()
video_writer.release()
self.logger.debug("Rec ended, frames-"+str(frame_ctr))
except Exception as e:
self.logger.critical("Error in proc_q ")
self.logger.critical(str(e))
self.logger.critical(str(traceback.format_exc()) )
def record_vid(self):
try:
grab_thread=threading.Thread(target=self.grab_CAM,args=())
grab_thread.start()
time.sleep(1)
q_proc_thread=threading.Thread(target=self.proc_q,args=())
q_proc_thread.start()
grab_thread.join()
self.ser.write(b'0')
self.logger.debug("Lights off")
q_proc_thread.join()
self.logger.debug("Video rec Ended")
except Exception as e:
self.logger.debug("Error in record video")
self.logger.debug(str(e))
self.logger.debug(str(traceback.format_exc()))
self.ser.write(b'0')
self.logger.debug("Lights off")
def main(self):
try:
while True:
if os.path.exists(os.path.join(self.TRIGGER_FILE_PATH,self.TRIGGER_FILE_NAME)):
if self.ARDUINO_STATE=="NT":
#Start recording
train_nm=""
with open(os.path.join(self.TRIGGER_FILE_PATH,self.TRIGGER_FILE_NAME)) as fl:
train_nm=str(fl.read()).replace(" ","").replace("\n","")
fl.close()
train_nm=train_nm+".avi"
self.ser.write(b'1')
self.logger.debug("Lights on")
self.ARDUINO_STATE="T"
self.record_vid()
#move video to proc location
#set train_name to video file
os.rename(os.path.join(self.TMP_VID_REC_LOC,self.video_fileName),os.path.join(self.SAVE_VID_LOC,train_nm))
videoFLmoved="Video File Moved from {fromloc} to {toloc}".format(fromloc=str(os.path.join(self.TMP_VID_REC_LOC,self.video_fileName)),toloc=str(os.path.join(self.SAVE_VID_LOC,train_nm)))
self.logger.debug(videoFLmoved)
exit(1)
else:
self.ARDUINO_STATE="NT"
except Exception as e:
self.logger.debug("Error in Main loop")
self.logger.debug(str(e))
self.logger.debug(str(traceback.format_exc()))
self.ser.write(b'0')
self.logger.debug("Lights off")
exit(1)
if __name__ == '__main__':
MVRecordingModuleObj = MVRecordingModule("MV_RECORDING_MODULE_CONFIG.xml")
MVRecordingModuleObj.main()
some first thoughts from looking through your code ( only enabled proper code formatting in your comment )
a) threading: you access global data structures without locking between multiple threads! b) efficiency: H.264 has yuv as input. you configure your camera to output BayerRaw ... convert to RGB which will be internally converted by ffmpeg to yuv before encoding.... if you have to run with BayerRaw because of framerate issues, only convert to yuv
c) for reference also a compact recording implementation in issue https://github.com/basler/pypylon/issues/113#issuecomment-543774545 Basic idea is, that you don't have to use multiple threads. Grabbing is running in background pylon threads and the encoding is also running multithreaded in ffmpeg background.