def startThreads(self): self.grabFrameOK = False self.listenerThread = SocketListener(self.requestQ, self.messageQ) error = self.listenerThread.test() if error: wx.MessageBox( 'Socket Error:\n\n{}\n\nIs another CrossMgrVideo or CrossMgrCamera running on this computer?' .format(error), "Socket Error", wx.OK | wx.ICON_ERROR) wx.Exit() self.dbWriterThread = threading.Thread(target=DBWriter, args=(self.dbWriterQ, )) self.dbWriterThread.daemon = True self.dbReaderThread = threading.Thread(target=DBReader, args=(self.dbReaderQ, self.setFinishStripJpgs)) self.dbReaderThread.daemon = True self.fcb = FrameCircBuf(int(self.bufferSecs * self.fps)) self.listenerThread.start() self.dbWriterThread.start() self.dbReaderThread.start() self.grabFrameOK = True self.messageQ.put(('threads', 'Successfully Launched')) return True
def __init__( self, camera, refTime=None, dirName='.', fps=25, bufferSeconds=4.0, owner=None, burstMode=True ): self.camera = camera self.refTime = refTime if refTime is not None else now() self.dirName = dirName self.fps = fps self.frameMax = int(fps * bufferSeconds) self.frameDelay = 1.0 / fps self.frameDelayTimeDelta = timedelta(seconds=self.frameDelay) self.frameSaver = None self.fcb = FrameCircBuf() self.owner = owner # Destination to send photos after they are taken. self.burstMode = burstMode self.timer = CallbackTimer( self.recordVideoFrame ) self.reset()
def CamServer(qIn, pWriter, camInfo=None): sendUpdates = {} tsSeen = set() camInfo = camInfo or {} backlog = [] def pWriterSend(msg): try: pWriter.send(msg) except MemoryError as e: print('pWriterSend: ', e) while 1: with VideoCaptureManager(**camInfo) as cap: time.sleep(0.25) frameCount = 0 inCapture = False doSnapshot = False tsSeen.clear() fcb = FrameCircBuf(int(camInfo.get('fps', 30) * bufferSeconds)) tsQuery = tsMax = now() keepCapturing = 1 while keepCapturing: # Capture frame-by-frame try: ret, frame = cap.read() except KeyboardInterrupt: return ts = now() if not ret: break fcb.append(ts, frame) try: m = qIn.get_nowait() while 1: cmd = m['cmd'] if cmd == 'query': if m['tStart'] > ts: # Reschedule future requests for later. Timer((m['tStart'] - ts).total_seconds(), qIn.put, m).start() continue if (ts - tsQuery).total_seconds( ) > bufferSeconds or len(tsSeen) > 5000: tsSeen.clear() tsQuery = ts times, frames = fcb.getTimeFrames( m['tStart'], m['tEnd'], tsSeen) backlog.extend( (t, f) for t, f in zip(times, frames)) if m['tEnd'] > tsMax: tsMax = m['tEnd'] elif cmd == 'start_capture': if 'tStart' in m: times, frames = fcb.getTimeFrames( m['tStart'], ts, tsSeen) backlog.extend( (t, f) for t, f in zip(times, frames)) inCapture = True elif cmd == 'stop_capture': inCapture = False elif cmd == 'snapshot': doSnapshot = True elif cmd == 'send_update': sendUpdates[m['name']] = m['freq'] elif cmd == 'cancel_update': if 'name' not in m: sendUpdates.clear() else: sendUpdates.pop(m['name'], None) elif cmd == 'cam_info': camInfo = m['info'] or {} keepCapturing = 0 break elif cmd == 'terminate': pWriterSend({'cmd': 'terminate'}) return else: assert False, 'Unknown Command' m = qIn.get_nowait() except Empty: pass if tsMax > ts or inCapture: backlog.append((ts, frame)) tsSeen.add(ts) # Don't send too many frames at a time. We don't want to overwhelm the pipe and lose frames. # Always ensure that the most recent frame is sent so any update requests can be satisfied with the last frame. if backlog: pWriterSend({ 'cmd': 'response', 'ts_frames': backlog[-transmitFramesMax:] }) # Send update messages. If there was a backlog, don't send the frame as we can use the last frame sent. updateFrame = None if backlog and backlog[-1][ 0] == ts else frame for name, f in sendUpdates.items(): #if frameCount % (f if backlog else 8) == 0: if frameCount % f == 0: pWriterSend({ 'cmd': 'update', 'name': name, 'frame': updateFrame }) updateFrame = None if doSnapshot: pWriterSend({ 'cmd': 'snapshot', 'ts': ts, 'frame': updateFrame }) doSnapshot = False del backlog[-transmitFramesMax:] frameCount += 1
def __init__(self, parent, id=wx.ID_ANY, title='', size=(1000, 800)): wx.Frame.__init__(self, parent, id, title, size=size) self.fps = 25 self.frameDelay = 1.0 / self.fps self.bufferSecs = 10 self.tFrameCount = self.tLaunch = self.tLast = now() self.frameCount = 0 self.frameCountUpdate = self.fps * 2 self.fpsActual = 0.0 self.fpt = timedelta(seconds=0) self.fcb = FrameCircBuf(self.bufferSecs * self.fps) self.config = wx.Config( appName="CrossMgrCamera", vendorName="SmartCyclingSolutions", #style=wx.CONFIG_USE_LOCAL_FILE ) self.requestQ = Queue() # Select photos from photobuf. self.writerQ = Queue(400) # Selected photos waiting to be written out. self.ftpQ = Queue() # Photos waiting to be ftp'd. self.renamerQ = Queue( ) # Photos waiting to be renamed and possibly ftp'd. self.messageQ = Queue( ) # Collection point for all status/failure messages. self.SetBackgroundColour(wx.Colour(232, 232, 232)) mainSizer = wx.BoxSizer(wx.VERTICAL) self.primaryImage = ScaledImage(self, style=wx.BORDER_SUNKEN, size=(imageWidth, imageHeight)) self.beforeImage = ScaledImage(self, style=wx.BORDER_SUNKEN, size=(imageWidth, imageHeight)) self.afterImage = ScaledImage(self, style=wx.BORDER_SUNKEN, size=(imageWidth, imageHeight)) self.beforeAfterImages = [self.beforeImage, self.afterImage] #------------------------------------------------------------------------------------------------ headerSizer = wx.BoxSizer(wx.HORIZONTAL) self.logo = Utils.GetPngBitmap('CrossMgrHeader.png') headerSizer.Add(wx.StaticBitmap(self, wx.ID_ANY, self.logo)) self.title = wx.StaticText(self, label='CrossMgr Camera\nVersion {}'.format( AppVerName.split()[1]), style=wx.ALIGN_RIGHT) self.title.SetFont( wx.Font((0, 28), wx.FONTFAMILY_SWISS, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL)) headerSizer.Add(self.title, flag=wx.ALL, border=10) #------------------------------------------------------------------------------ self.cameraDeviceLabel = wx.StaticText(self, label='Camera Device:') self.cameraDevice = wx.StaticText(self) boldFont = self.cameraDevice.GetFont() boldFont.SetWeight(wx.BOLD) self.cameraDevice.SetFont(boldFont) self.cameraResolution = wx.StaticText(self) self.cameraResolution.SetFont(boldFont) bitmap = wx.Bitmap(clipboard_xpm) self.copyLogToClipboard = wx.BitmapButton(self, bitmap=bitmap) self.copyLogToClipboard.Bind(wx.EVT_BUTTON, self.onCopyLogToClipboard) self.reset = wx.Button(self, label="Reset Camera") self.reset.Bind(wx.EVT_BUTTON, self.resetCamera) cameraDeviceSizer = wx.BoxSizer(wx.HORIZONTAL) cameraDeviceSizer.Add(self.cameraDeviceLabel, flag=wx.ALIGN_CENTRE_VERTICAL | wx.ALIGN_RIGHT) cameraDeviceSizer.Add(self.cameraDevice, flag=wx.ALIGN_CENTRE_VERTICAL | wx.LEFT, border=8) cameraDeviceSizer.Add(self.cameraResolution, flag=wx.ALIGN_CENTRE_VERTICAL | wx.LEFT, border=8) cameraDeviceSizer.Add(self.copyLogToClipboard, flag=wx.ALIGN_CENTRE_VERTICAL | wx.LEFT, border=8) cameraDeviceSizer.Add(self.reset, flag=wx.ALIGN_CENTRE_VERTICAL | wx.LEFT, border=24) #------------------------------------------------------------------------------ self.targetProcessingTimeLabel = wx.StaticText(self, label='Target Frames:') self.targetProcessingTime = wx.StaticText(self, label=u'{:.3f}'.format( self.fps)) self.targetProcessingTime.SetFont(boldFont) self.targetProcessingTimeUnit = wx.StaticText(self, label='per sec') self.framesPerSecondLabel = wx.StaticText(self, label='Actual Frames:') self.framesPerSecond = wx.StaticText(self, label='25.000') self.framesPerSecond.SetFont(boldFont) self.framesPerSecondUnit = wx.StaticText(self, label='per sec') self.availableMsPerFrameLabel = wx.StaticText( self, label='Available Time Per Frame:') self.availableMsPerFrame = wx.StaticText(self, label=u'{:.0f}'.format( 1000.0 * self.frameDelay)) self.availableMsPerFrame.SetFont(boldFont) self.availableMsPerFrameUnit = wx.StaticText(self, label='ms') self.frameProcessingTimeLabel = wx.StaticText( self, label='Actual Frame Processing:') self.frameProcessingTime = wx.StaticText(self, label='20') self.frameProcessingTime.SetFont(boldFont) self.frameProcessingTimeUnit = wx.StaticText(self, label='ms') pfgs = wx.FlexGridSizer(rows=0, cols=6, vgap=4, hgap=8) fRight = wx.ALIGN_CENTRE_VERTICAL | wx.ALIGN_RIGHT fLeft = wx.ALIGN_CENTRE_VERTICAL #------------------- Row 1 ------------------------------ pfgs.Add(self.targetProcessingTimeLabel, flag=fRight) pfgs.Add(self.targetProcessingTime, flag=fRight) pfgs.Add(self.targetProcessingTimeUnit, flag=fLeft) pfgs.Add(self.availableMsPerFrameLabel, flag=fRight) pfgs.Add(self.availableMsPerFrame, flag=fRight) pfgs.Add(self.availableMsPerFrameUnit, flag=fLeft) #------------------- Row 2 ------------------------------ pfgs.Add(self.framesPerSecondLabel, flag=fRight) pfgs.Add(self.framesPerSecond, flag=fRight) pfgs.Add(self.framesPerSecondUnit, flag=fLeft) pfgs.Add(self.frameProcessingTimeLabel, flag=fRight) pfgs.Add(self.frameProcessingTime, flag=fRight) pfgs.Add(self.frameProcessingTimeUnit, flag=fLeft) statsSizer = wx.BoxSizer(wx.VERTICAL) statsSizer.Add(cameraDeviceSizer) statsSizer.Add(pfgs, flag=wx.TOP, border=8) headerSizer.Add(statsSizer, flag=wx.ALL, border=4) mainSizer.Add(headerSizer) self.messagesText = wx.TextCtrl(self, style=wx.TE_READONLY | wx.TE_MULTILINE | wx.HSCROLL, size=(350, imageHeight)) self.messageManager = MessageManager(self.messagesText) border = 2 row1Sizer = wx.BoxSizer(wx.HORIZONTAL) row1Sizer.Add(self.primaryImage, flag=wx.ALL, border=border) row1Sizer.Add(self.messagesText, 1, flag=wx.TOP | wx.BOTTOM | wx.RIGHT | wx.EXPAND, border=border) mainSizer.Add(row1Sizer, 1, flag=wx.EXPAND) row2Sizer = wx.BoxSizer(wx.HORIZONTAL) row2Sizer.Add(self.beforeImage, flag=wx.LEFT | wx.RIGHT | wx.BOTTOM, border=border) row2Sizer.Add(self.afterImage, flag=wx.RIGHT | wx.BOTTOM, border=border) mainSizer.Add(row2Sizer) #------------------------------------------------------------------------------------------------ # Create a timer to update the frame loop. # self.timer = wx.Timer() self.timer.Bind(wx.EVT_TIMER, self.frameLoop) self.Bind(wx.EVT_CLOSE, self.onCloseWindow) self.readOptions() self.SetSizerAndFit(mainSizer) # Start the message reporting thread so we can see what is going on. self.messageThread = threading.Thread(target=self.showMessages) self.messageThread.daemon = True self.messageThread.start() self.grabFrameOK = False for i in [self.primaryImage, self.beforeImage, self.afterImage]: i.SetTestImage() # Start the frame loop. delayAdjustment = 0.80 if 'win' in sys.platform else 0.98 ms = int(1000 * self.frameDelay * delayAdjustment) self.timer.Start(ms, False)
def __init__(self, parent, id=wx.ID_ANY, title='', size=(1000, 800)): wx.Frame.__init__(self, parent, id, title, size=size) self.db = Database() self.fps = 25 self.frameDelay = 1.0 / self.fps self.bufferSecs = 10 self.xFinish = None self.tFrameCount = self.tLaunch = self.tLast = now() self.frameCount = 0 self.frameCountUpdate = int(self.fps * 2) self.fpsActual = 0.0 self.fpt = timedelta(seconds=0) self.iTriggerSelect = None self.triggerInfo = None self.captureTimer = wx.CallLater(10, self.stopCapture) self.fcb = FrameCircBuf(self.bufferSecs * self.fps) self.config = wx.Config( appName="CrossMgrVideo", vendorName="SmartCyclingSolutions", #style=wx.CONFIG_USE_LOCAL_FILE ) self.requestQ = Queue() # Select photos from photobuf. self.dbWriterQ = Queue() # Photos waiting to be written self.dbReaderQ = Queue() # Photos read as requested from user. self.messageQ = Queue( ) # Collection point for all status/failure messages. self.SetBackgroundColour(wx.Colour(232, 232, 232)) mainSizer = wx.BoxSizer(wx.VERTICAL) #------------------------------------------------------------------------------------------------ headerSizer = wx.BoxSizer(wx.HORIZONTAL) self.logo = Utils.GetPngBitmap('CrossMgrHeader.png') headerSizer.Add(wx.StaticBitmap(self, wx.ID_ANY, self.logo)) self.title = wx.StaticText(self, label='CrossMgr Video\nVersion {}'.format( AppVerName.split()[1]), style=wx.ALIGN_RIGHT) self.title.SetFont( wx.Font((0, 28), wx.FONTFAMILY_SWISS, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL)) headerSizer.Add(self.title, flag=wx.ALL, border=10) #------------------------------------------------------------------------------ self.cameraDeviceLabel = wx.StaticText(self, label='Camera Device:') self.cameraDevice = wx.StaticText(self) boldFont = self.cameraDevice.GetFont() boldFont.SetWeight(wx.BOLD) self.cameraDevice.SetFont(boldFont) self.cameraResolution = wx.StaticText(self) self.cameraResolution.SetFont(boldFont) self.reset = wx.Button(self, label="Reset Camera") self.reset.Bind(wx.EVT_BUTTON, self.resetCamera) self.manage = wx.Button(self, label="Manage Database") self.manage.Bind(wx.EVT_BUTTON, self.manageDatabase) self.test = wx.Button(self, label="Test") self.test.Bind(wx.EVT_BUTTON, self.onTest) self.focus = wx.Button(self, label="Focus...") self.focus.Bind(wx.EVT_BUTTON, self.onFocus) cameraDeviceSizer = wx.BoxSizer(wx.HORIZONTAL) cameraDeviceSizer.Add(self.cameraDeviceLabel, flag=wx.ALIGN_CENTRE_VERTICAL | wx.ALIGN_RIGHT) cameraDeviceSizer.Add(self.cameraDevice, flag=wx.ALIGN_CENTRE_VERTICAL | wx.LEFT, border=8) cameraDeviceSizer.Add(self.cameraResolution, flag=wx.ALIGN_CENTRE_VERTICAL | wx.LEFT, border=8) cameraDeviceSizer.Add(self.reset, flag=wx.ALIGN_CENTRE_VERTICAL | wx.LEFT, border=32) cameraDeviceSizer.Add(self.manage, flag=wx.ALIGN_CENTRE_VERTICAL | wx.LEFT, border=16) cameraDeviceSizer.Add(self.test, flag=wx.ALIGN_CENTRE_VERTICAL | wx.LEFT, border=16) cameraDeviceSizer.Add(self.focus, flag=wx.ALIGN_CENTRE_VERTICAL | wx.LEFT, border=16) #------------------------------------------------------------------------------ self.targetProcessingTimeLabel = wx.StaticText(self, label='Target Frames:') self.targetProcessingTime = wx.StaticText(self, label=u'{:.3f}'.format( self.fps)) self.targetProcessingTime.SetFont(boldFont) self.targetProcessingTimeUnit = wx.StaticText(self, label='/ sec') self.framesPerSecondLabel = wx.StaticText(self, label='Actual Frames:') self.framesPerSecond = wx.StaticText(self, label='25.000') self.framesPerSecond.SetFont(boldFont) self.framesPerSecondUnit = wx.StaticText(self, label='/ sec') self.availableMsPerFrameLabel = wx.StaticText( self, label='Available Time Per Frame:') self.availableMsPerFrame = wx.StaticText(self, label=u'{:.0f}'.format( 1000.0 * self.frameDelay)) self.availableMsPerFrame.SetFont(boldFont) self.availableMsPerFrameUnit = wx.StaticText(self, label='ms') self.frameProcessingTimeLabel = wx.StaticText( self, label='Frame Processing Time:') self.frameProcessingTime = wx.StaticText(self, label='20') self.frameProcessingTime.SetFont(boldFont) self.frameProcessingTimeUnit = wx.StaticText(self, label='ms') pfgs = wx.FlexGridSizer(rows=0, cols=6, vgap=4, hgap=8) fRight = wx.ALIGN_CENTRE_VERTICAL | wx.ALIGN_RIGHT fLeft = wx.ALIGN_CENTRE_VERTICAL #------------------- Row 1 ------------------------------ pfgs.Add(self.targetProcessingTimeLabel, flag=fRight) pfgs.Add(self.targetProcessingTime, flag=fRight) pfgs.Add(self.targetProcessingTimeUnit, flag=fLeft) pfgs.Add(self.availableMsPerFrameLabel, flag=fRight) pfgs.Add(self.availableMsPerFrame, flag=fRight) pfgs.Add(self.availableMsPerFrameUnit, flag=fLeft) #------------------- Row 2 ------------------------------ pfgs.Add(self.framesPerSecondLabel, flag=fRight) pfgs.Add(self.framesPerSecond, flag=fRight) pfgs.Add(self.framesPerSecondUnit, flag=fLeft) pfgs.Add(self.frameProcessingTimeLabel, flag=fRight) pfgs.Add(self.frameProcessingTime, flag=fRight) pfgs.Add(self.frameProcessingTimeUnit, flag=fLeft) statsSizer = wx.BoxSizer(wx.VERTICAL) statsSizer.Add(cameraDeviceSizer) statsSizer.Add(pfgs, flag=wx.TOP, border=8) headerSizer.Add(statsSizer, flag=wx.ALL, border=4) mainSizer.Add(headerSizer) #------------------------------------------------------------------------------------------------ self.finishStrip = FinishStripPanel(self, size=(-1, wx.GetDisplaySize()[1] // 2)) self.finishStrip.finish.Bind(wx.EVT_RIGHT_DOWN, self.onRightClick) self.primaryImage = ScaledImage(self, style=wx.BORDER_SUNKEN, size=(imageWidth, imageHeight)) self.primaryImage.SetTestImage() self.focusDialog = FocusDialog(self) hsDate = wx.BoxSizer(wx.HORIZONTAL) hsDate.Add(wx.StaticText(self, label='Show Triggers for'), flag=wx.ALIGN_CENTER_VERTICAL) tQuery = now() self.date = wx.adv.DatePickerCtrl( self, dt=wx.DateTime.FromDMY(tQuery.day, tQuery.month - 1, tQuery.year), style=wx.adv.DP_DROPDOWN | wx.adv.DP_SHOWCENTURY) self.date.Bind(wx.adv.EVT_DATE_CHANGED, self.onQueryDateChanged) hsDate.Add(self.date, flag=wx.LEFT, border=2) hsDate.Add(wx.StaticText(self, label='Filter by Bib'), flag=wx.ALIGN_CENTER_VERTICAL | wx.LEFT, border=12) self.bib = wx.lib.intctrl.IntCtrl(self, style=wx.TE_PROCESS_ENTER, size=(64, -1), min=1, allow_none=True, value=None) self.bib.Bind(wx.EVT_TEXT_ENTER, self.onQueryBibChanged) hsDate.Add(self.bib, flag=wx.LEFT, border=2) self.tsQueryLower = datetime(tQuery.year, tQuery.month, tQuery.day) self.tsQueryUpper = self.tsQueryLower + timedelta(days=1) self.bibQuery = None self.triggerList = AutoWidthListCtrl( self, style=wx.LC_REPORT | wx.BORDER_SUNKEN | wx.LC_SORT_ASCENDING) self.il = wx.ImageList(16, 16) self.sm_close = [] for bm in getCloseFinishBitmaps(): self.sm_close.append(self.il.Add(bm)) self.sm_up = self.il.Add(Utils.GetPngBitmap('SmallUpArrow.png')) self.sm_up = self.il.Add(Utils.GetPngBitmap('SmallUpArrow.png')) self.sm_dn = self.il.Add(Utils.GetPngBitmap('SmallDownArrow.png')) self.triggerList.SetImageList(self.il, wx.IMAGE_LIST_SMALL) headers = ['Time', 'Bib', 'Name', 'Team', 'Wave', 'km/h', 'mph'] for i, h in enumerate(headers): self.triggerList.InsertColumn( i, h, wx.LIST_FORMAT_RIGHT if h in ('Bib', 'km/h', 'mph') else wx.LIST_FORMAT_LEFT) self.itemDataMap = {} self.triggerList.Bind(wx.EVT_LIST_ITEM_SELECTED, self.onTriggerSelected) self.messagesText = wx.TextCtrl(self, style=wx.TE_READONLY | wx.TE_MULTILINE | wx.HSCROLL, size=(250, -1)) self.messageManager = MessageManager(self.messagesText) vsTriggers = wx.BoxSizer(wx.VERTICAL) vsTriggers.Add(hsDate) vsTriggers.Add(self.triggerList, 1, flag=wx.EXPAND | wx.TOP, border=2) #------------------------------------------------------------------------------------------------ mainSizer.Add(self.finishStrip, 1, flag=wx.EXPAND) border = 2 row1Sizer = wx.BoxSizer(wx.HORIZONTAL) row1Sizer.Add(self.primaryImage, flag=wx.ALL, border=border) row1Sizer.Add(vsTriggers, 1, flag=wx.TOP | wx.BOTTOM | wx.RIGHT | wx.EXPAND, border=border) row1Sizer.Add(self.messagesText, flag=wx.TOP | wx.BOTTOM | wx.RIGHT | wx.EXPAND, border=border) mainSizer.Add(row1Sizer, flag=wx.EXPAND) #------------------------------------------------------------------------------------------------ # Create a timer to update the frame loop. # self.timer = wx.Timer() self.timer.Bind(wx.EVT_TIMER, self.frameLoop) self.Bind(wx.EVT_CLOSE, self.onCloseWindow) self.readOptions() self.SetSizerAndFit(mainSizer) # Start the message reporting thread so we can see what is going on. self.messageThread = threading.Thread(target=self.showMessages) self.messageThread.daemon = True self.messageThread.start() self.grabFrameOK = False self.tsMax = None # Start the frame loop. delayAdjustment = 0.80 if 'win' in sys.platform else 0.98 ms = int(1000 * self.frameDelay * delayAdjustment) self.timer.Start(ms, False) wx.CallLater(300, self.refreshTriggers)