def startRecording(cIdx, oFormat, recFolder, fps, fSz, logFile, fpsLimit, ssIntv): # Define the codec and create VideoWriter object #fourcc = cv2.VideoWriter_fourcc(*'X264') fourcc = cv2.VideoWriter_fourcc(*'avc1') # for saving mp4 video #fourcc = cv2.VideoWriter_fourcc('x','v','i','d') log = "%s," % (get_time_stamp()) log += " Cam-%.2i recording starts" % (cIdx) log += " [%s]" % (oFormat) if oFormat == 'video': ofn = "output_%.2i_%s.mp4" % (cIdx, get_time_stamp()) ofn = path.join(recFolder, ofn) # get average of the past 10 fps records ofps = int(np.average(fps[:10])) # set 'out' as a video writer out = cv2.VideoWriter(ofn, fourcc, ofps, fSz, True) log += " [%s] [FPS: %i] [FPS-limit: %i]\n" % (ofn, ofps, fpsLimit) elif oFormat == 'image': ofn = "output_%.2i_%s" % (cIdx, get_time_stamp()) ofn = path.join(recFolder, ofn) # 'out' is used as an index of a image file out = 1 log += " [%s] [Snapshot-interval: %s]\n" % (ofn, str(ssIntv)) if not path.isdir(ofn): mkdir(ofn) writeFile(logFile, log) return out, ofn
def stopRecording(out, cIdx, logFile): if isinstance(out, cv2.VideoWriter): out.release() out = None ### log log = "%s," % (get_time_stamp()) log += " Cam-%.2i recording stops\n" % (cIdx) writeFile(logFile, log) return out
def toggleCamThread(self, ci): """ Start/Stop a cam thread Args: ci (int): Index of cam to start Returns: None """ if DEBUG: print("CamRecFrame.startCamThread()") if self.th[ci] == -1: # thread is not running ### update output format for Cam recording cho = wx.FindWindowByName("outputFormat_cho", self.panel["ui"]) outputFormat = cho.GetString(cho.GetSelection()) # video or image self.cams[ci].outputFormat = outputFormat if outputFormat == "video": ### update FPS limit for Cam recording w = wx.FindWindowByName("videoFPSlimit_spin", self.panel["ui"]) fpsLimit = str2num(w.GetValue(), 'float') if fpsLimit != None: self.cams[ci].fpsLimit = fpsLimit elif outputFormat == "image": ### update snapshot interval for Cam recording w = wx.FindWindowByName("ssIntv_spin", self.panel["ui"]) ssIntv = str2num(w.GetValue(), 'float') if ssIntv != None: self.cams[ci].ssIntv = ssIntv ### start Cam thread args = ( self.q2m, self.q2t[ci], self.recFolder, ) self.th[ci] = Thread(target=self.cams[ci].run, args=args) self.th[ci].start() ### start timer to check q2m ### (queued message from the running thread) if "chkQ2M" in self.timer.keys() and \ self.timer["chkQ2M"].IsRunning() == False: self.timer["chkQ2M"].Start(50) else: self.timer["chkQ2M"] = wx.Timer(self) self.Bind(wx.EVT_TIMER, self.chkQ2M, self.timer["chkQ2M"]) self.timer["chkQ2M"].Start(self.dispImgRefreshIntv) # log message log = "%s, Cam-%.2i thread started\n" % (get_time_stamp(), ci) else: ### stop Cam thread self.q2t[ci].put("quit", True, None) # send message to quit thread self.th[ci].join() self.th[ci] = -1 ### if no cam thread is running, stop chkQ2M timer as well. if self.th == [-1] * len(self.th): self.timer["chkQ2M"].Stop() # log message log = "%s, Cam-%.2i thread stopped\n" % (get_time_stamp(), ci) writeFile(self.logFile, log)
def runImgProc(self): """ Run image processing going through all files with all planned processing Args: None Returns: None """ if DEBUG: print("ImgProcsFrame.runImgProc()") msg = "This action will save all image files in the same" msg += " selected folder. If filenames are same, they will be REPLACED" msg += " by processed images.\n" msg += "Proceed?" dlg = PopupDialog(self, -1, "Warning", msg, flagOkayBtn=True, flagCancelBtn=True) if dlg.ShowModal() == wx.ID_CANCEL: return msg = "Processed files -----\n\n" # result message msg4log = "" # log message ### get image file extension user wants to use obj = wx.FindWindowByName("imgFormat_cho", self.panel["ui"]) imgExt = obj.GetString(obj.GetSelection()) if "original" in imgExt.lower(): imgExt = "" for i, fp in enumerate(self.fileList): # go through each file img = np.array(Image.open(fp)) # open image img = self.procImg(img) # process if imgExt != "": # if there's a specific image format user chose ### change file extension ext = "." + fp.split(".")[-1] if ext != imgExt: fp = fp.replace(ext, imgExt) Image.fromarray(img).save(fp) # save image pl = str(self.procList) pl = pl.strip("[]").replace(", ","/").replace("'","") msg4log += "%s, %s, %s\n"%(get_time_stamp(), fp, pl) msg += "%s\n\n"%(fp) writeFile(self.logFile, msg4log) # logging results wx.MessageBox(msg, 'Results', wx.OK)
def compareSF2cParam(self, sfParams): """ Retrieve parameters from wx.TextCtrl in UI and compare parameters of the captured sound fragment with them. Args: sfParams (dict): Sound parameters of a captured sound fragment. Returns: rslt (bool): True means that two sounds matched with given parameters. False means they didn't match. rsltTxt (str): String stored during processes of the function. It could be error message, information, etc. """ if DEBUG: print("PyListenerFrame.compareSF2cParam()") rslt = True # whether satisfying min. & max. value thresholds # of all checked parameters rsltTxt = "" tParams2c = {} # template WAV parameters to compare if self.pl.templFP == None: # there's no selected template WAV _txt = "! Template WAV is not selected." _txt += " No comparison was conducted. !" rsltTxt = "[%s]"%(_txt) writeFile(self.logFile, "%s, [MSG], %s\n"%(get_time_stamp(), _txt)) rslt = False return rslt, rsltTxt else: mm = ["min", "max"] for param in self.pl.compParamList: # through each comparison parameter name = "comp_" + param chkB = wx.FindWindowByName( name+"_chk", self.panel["ip_spT"] ) if chkB.GetValue() == False: continue # this parameter # is not checked. move on to the next parameter ### prepare min. and max. values (extracted from ### template WAV and editted by user) to compare ### with sound fragment params. for mmn in mm: _name = name + "_" + mmn txt = wx.FindWindowByName( _name, self.panel["ip_spT"] ) txtVal = txt.GetValue().strip() if txtVal == "": # the textCtrl is empty continue # move to the next one, # considering this one is satisfied try: th = float(txtVal) # threshold value except: # stop listening self.onBPButtonPress('startStopListening') _txt = "%s, [MSG],"%(get_time_stamp()) _txt += " !!! Value of %s is not a number."%(_name) _txt += " Comparison aborted. !!!\n" writeFile( self.logFile, _txt) show_msg(_txt) rsltTxt += _txt rslt = False return rslt, rsltTxt tParams2c[param+"_"+mmn] = th if len(tParams2c) > 0: # compare sound fragment parmaeters rslt, _txt = self.pl.compareParamsOfSF2T(sfParams, tParams2c) rsltTxt += "%s\n"%(_txt) return rslt, rsltTxt
def __init__(self): if DEBUG: print("ImgProcsFrame.__init__()") ### init frame w_pos = [0, 25] wg = wx.Display(0).GetGeometry() wSz = (wg[2], int(wg[3]*0.9)) wx.Frame.__init__( self, None, -1, "pyImgProc v.%s"%(__version__), pos = tuple(w_pos), size = tuple(wSz), style=wx.DEFAULT_FRAME_STYLE^(wx.RESIZE_BORDER|wx.MAXIMIZE_BOX), ) self.SetBackgroundColour('#333333') ### set app icon self.tbIcon = wx.adv.TaskBarIcon(iconType=wx.adv.TBI_DOCK) icon = wx.Icon("icon.ico") self.tbIcon.SetIcon(icon) ##### beginning of setting up attributes ----- self.w_pos = w_pos # window position self.wSz = wSz # window size self.fonts = getWXFonts(initFontSz=8, numFonts=3) pi = self.setPanelInfo() self.pi = pi # pnael information self.gbs = {} # for GridBagSizer self.panel = {} # panels self.timer = {} # timers self.selectedFolders = [] # list of selected folders self.fileList = [] # file list of images to process self.imgFormats = [".bmp", ".eps", ".gif", ".jpg", ".pcx", ".png", ".tiff", ".webp"] # image formats for # saving after image processing self.imgFormats = sorted(self.imgFormats) self.imgFormats.insert(0, "Use original file extension as it is") self.imgProcOptions = [ 'greyscale', 'crop', 'crop_ratio', 'masking', 'resize', 'resize_ratio', 'rotate', 'flip', 'brighten', 'darken', 'text', ] # image processing options self.ipParams = dict( greyscale = [], crop = ['x', 'y', 'w', 'h'], crop_ratio = ['x', 'y', 'w', 'h'], masking = ['fill-color'], resize = ['w', 'h'], resize_ratio = ['w', 'h'], rotate = ['deg', 'expand'], flip = ['direction'], brighten = ['value'], darken = ['value'], text = ['text', 'x', 'y', 'font-size', 'color'], ) # parameters for each image processing self.ipParamDesc = dict( greyscale = [], crop = [ 'x-coordinate to start (pixel)', 'y-coordinate to start (pixel)', 'width of cropped image (pixel)', 'height of cropped image (pixel)' ], crop_ratio = [ 'x-coordinate to start (0.0-1.0)', 'y-coordinate to start (0.0-1.0)', 'width of cropped image (0.0-1.0)', 'height of cropped image (0.0-1.0)' ], masking = [ 'color to fill where black in masking image (hexadecimal)', ], resize = [ 'width of image (pixel)', 'height of image (pixel)' ], resize_ratio = [ 'width in float (1.0 = original size)', 'height in float (1.0 = original size)', ], rotate = [ 'degree to rotate (0-360)', 'expand to contain rotated image (0 or 1)', ], flip = [ 'direction (0-2; 0:horizontal, 1:vertical, 2:both)', ], brighten = [ 'pixel value to add' ], darken = [ 'pixel value to subtract' ], text = [ 'text to insert', 'x-coordinate (0.0-1.0)', 'y-coordinate (0.0-1.0)', 'font size (integer)', 'font color (hexadecimal)', ], ) # description of parameters self.ipParamVal = dict( greyscale = [], crop = [0, 0, 1, 1], crop_ratio = [0.0, 0.0, 0.5, 0.5], masking = ['#000000'], resize = [1, 1], resize_ratio = [0.1, 0.1], rotate = [0, 0], flip = [0], brighten = [20], darken = [20], text = ['', 0.0, 0.0, 12, '#000000'], ) # default value of each parameter # max. number of parameters among all processes self.mNumParam = -1 for k in self.ipParams.keys(): n = len(self.ipParams[k]) if self.mNumParam < n: self.mNumParam = copy(n) self.iImgArr = None # numpy array of input image self.oImgArr = None # numpy array of output image self.logFile = "log_pyImgProc.txt" # extension list to recognize as an image file for processing self.extList = ['bmp', 'png', 'jpg', 'gif', 'pcx', 'tif', 'tiff'] self.procList = [] # image processing list to execute self.maskFP = "mask.png" # masking image ##### end of setting up attributes ----- ### make log file logHeader = "Timestamp, Image file name, Processes\n" logHeader += "# ----------------------------------------\n" if not path.isfile(self.logFile): # log file doesn't exist writeFile(self.logFile, logHeader) # write header ### create panels for pk in pi.keys(): self.panel[pk] = SPanel.ScrolledPanel( self, name="%s_panel"%(pk), pos=pi[pk]["pos"], size=pi[pk]["sz"], style=pi[pk]["style"], ) self.panel[pk].SetBackgroundColour(pi[pk]["bgCol"]) #if pk in ["ip", "op"]: # self.panel[pk].Bind(wx.EVT_PAINT, self.onPaint) ##### beginning of setting up UI panel interface ----- bw = 5 # border width for GridBagSizer nCol = 4 # number columns hlSz = (pi["ui"]["sz"][0]-50, -1) # size of horizontal line separator vlSz = (-1, 20) # size of vertical line seprator self.gbs["ui"] = wx.GridBagSizer(0,0) row = 0 col = 0 btn = wx.Button( self.panel["ui"], -1, label="Select folders", name="selFolders_btn", ) btn.Bind(wx.EVT_LEFT_DOWN, self.onButtonPressDown) add2gbs(self.gbs["ui"], btn, (row,col), (1,1)) col += 1 chk = wx.CheckBox( self.panel["ui"], -1, label="Include sub-folders", name="subFolders_chk", ) chk.SetValue(False) add2gbs(self.gbs["ui"], chk, (row,col), (1,1)) row += 1; col = 0 sTxt = setupStaticText( self.panel["ui"], "Selected folders", font=self.fonts[2], ) add2gbs(self.gbs["ui"], sTxt, (row,col), (1,nCol)) row += 1; col = 0 txt = wx.TextCtrl( self.panel["ui"], -1, value="[EMPTY]; Please select folders", name="selDir_txt", size=(hlSz[0], 75), style=wx.TE_MULTILINE|wx.TE_READONLY, ) txt.SetBackgroundColour('#999999') add2gbs(self.gbs["ui"], txt, (row,col), (1,nCol)) row += 1; col = 0 add2gbs(self.gbs["ui"], wx.StaticLine(self.panel["ui"], -1, size=hlSz, style=wx.LI_HORIZONTAL), (row,col), (1,nCol)) # horizontal line separator row += 1; col = 0 lbl = "Target files (you can use wildcard characters)" sTxt = setupStaticText( self.panel["ui"], lbl, font=self.fonts[2], ) add2gbs(self.gbs["ui"], sTxt, (row,col), (1,nCol)) row += 1; col = 0 lbl = "Allowed extensions: %s"%(str(self.extList)) sTxt = setupStaticText( self.panel["ui"], lbl, font=self.fonts[1], ) add2gbs(self.gbs["ui"], sTxt, (row,col), (1,nCol)) row += 1; col = 0 txt = wx.TextCtrl( self.panel["ui"], -1, value="*.*", name="targetFN_txt", size=(hlSz[0], -1), style=wx.TE_PROCESS_ENTER, ) txt.Bind(wx.EVT_TEXT_ENTER, self.onEnteredInTC) add2gbs(self.gbs["ui"], txt, (row,col), (1,nCol)) row += 1; col = 0 add2gbs(self.gbs["ui"], wx.StaticLine(self.panel["ui"], -1, size=hlSz, style=wx.LI_HORIZONTAL), (row,col), (1,nCol)) # horizontal line separator row += 1; col = 0 sTxt = setupStaticText( self.panel["ui"], "List of files to be processed", font=self.fonts[2], ) add2gbs(self.gbs["ui"], sTxt, (row,col), (1,nCol)) row += 1; col = 0 lstCtrl = wx.ListCtrl( self.panel["ui"], -1, name="selFile_lst", size=(hlSz[0], 100), style=wx.LC_REPORT|wx.LC_SINGLE_SEL, ) # selected files to be processed lstCtrl.AppendColumn("FilePath") lstCtrl.SetColumnWidth(0, lstCtrl.GetSize()[0]) lstCtrl.Bind(wx.EVT_LIST_ITEM_SELECTED, self.onItemSelectedInLC) add2gbs(self.gbs["ui"], lstCtrl, (row,col), (1,nCol)) row += 1; col = 0 add2gbs(self.gbs["ui"], wx.StaticLine(self.panel["ui"], -1, size=hlSz, style=wx.LI_HORIZONTAL), (row,col), (1,nCol)) # horizontal line separator row += 1; col = 0 sTxt = setupStaticText( self.panel["ui"], "Processes to apply on each image", font=self.fonts[2], ) add2gbs(self.gbs["ui"], sTxt, (row,col), (1,nCol)) row += 1; col = 0 cho = wx.Choice( self.panel["ui"], -1, name="imgProcOption_cho", choices=self.imgProcOptions, ) cho.Bind(wx.EVT_CHOICE, self.onChoice) add2gbs(self.gbs["ui"], cho, (row,col), (1,1)) row += 1; col = 0 lstCtrl = wx.ListCtrl( self.panel["ui"], -1, name="proc_lst", size=(hlSz[0], 100), style=wx.LC_REPORT|wx.LC_SINGLE_SEL, ) # processes to apply on each image colW = lstCtrl.GetSize()[0]/(self.mNumParam+1) # set column width lstCtrl.AppendColumn("Process") for i in range(self.mNumParam): # add column name for parameter lstCtrl.AppendColumn("Param%i"%(i)) lstCtrl.SetColumnWidth(i, colW) lstCtrl.Bind(wx.EVT_LIST_ITEM_SELECTED, self.onItemSelectedInLC) add2gbs(self.gbs["ui"], lstCtrl, (row,col), (1,nCol)) row += 1; col = 0 btn = wx.Button( self.panel["ui"], -1, label="Clear selected", name="clearProc_btn", ) btn.Bind(wx.EVT_LEFT_DOWN, self.onButtonPressDown) add2gbs(self.gbs["ui"], btn, (row,col), (1,1)) col += 1 btn = wx.Button( self.panel["ui"], -1, label="Clear all", name="clearAllProc_btn", ) btn.Bind(wx.EVT_LEFT_DOWN, self.onButtonPressDown) add2gbs(self.gbs["ui"], btn, (row,col), (1,1)) col += 1 btn = wx.Button( self.panel["ui"], -1, label="Move Up", name="moveProcUp_btn", ) btn.Bind(wx.EVT_LEFT_DOWN, self.onButtonPressDown) add2gbs(self.gbs["ui"], btn, (row,col), (1,1)) col += 1 btn = wx.Button( self.panel["ui"], -1, label="Move Down", name="moveProcDown_btn", ) btn.Bind(wx.EVT_LEFT_DOWN, self.onButtonPressDown) add2gbs(self.gbs["ui"], btn, (row,col), (1,1)) row += 1; col = 0 sTxt = setupStaticText(self.panel["ui"], " ", font=self.fonts[1]) add2gbs(self.gbs["ui"], sTxt, (row,col), (1,1)) col += 1 sTxt = setupStaticText( self.panel["ui"], "", name="processName_sTxt", font=self.fonts[1]) add2gbs(self.gbs["ui"], sTxt, (row,col), (1,1)) for i in range(self.mNumParam): row += 1; col = 0 sTxt = setupStaticText(self.panel["ui"], "", name="param%i_sTxt"%(i), font=self.fonts[1]) add2gbs(self.gbs["ui"], sTxt, (row,col), (1,3)) sTxt.Hide() col += 3 txt = wx.TextCtrl(self.panel["ui"], -1, value="", name="param%i_txt"%(i), size=(100, -1)) add2gbs(self.gbs["ui"], txt, (row,col), (1,1)) txt.Hide() row += 1; col = 0 col += 3 btn = wx.Button(self.panel["ui"], -1, label="Update", name="updateParam_btn") btn.Bind(wx.EVT_LEFT_DOWN, self.onButtonPressDown) add2gbs(self.gbs["ui"], btn, (row,col), (1,1)) btn.Hide() row += 1; col = 0 sTxt = setupStaticText(self.panel["ui"], "File-format:", font=self.fonts[1]) add2gbs(self.gbs["ui"], sTxt, (row,col), (1,1)) col += 1 cho = wx.Choice( self.panel["ui"], -1, name="imgFormat_cho", choices=self.imgFormats, ) add2gbs(self.gbs["ui"], cho, (row,col), (1,nCol-1)) row += 1; col = 0 btn = wx.Button( self.panel["ui"], -1, label="Process & save all files", name="run_btn", size=(hlSz[0],-1), ) btn.Bind(wx.EVT_LEFT_DOWN, self.onButtonPressDown) add2gbs(self.gbs["ui"], btn, (row,col), (1,nCol)) row += 1; col = 0 sTxt = setupStaticText(self.panel["ui"], " ", font=self.fonts[1]) add2gbs(self.gbs["ui"], sTxt, (row,col), (1,nCol)) self.panel["ui"].SetSizer(self.gbs["ui"]) self.gbs["ui"].Layout() self.panel["ui"].SetupScrolling() ##### end of setting up UI panel interface ----- ##### beginning of setting up input image panel ----- self.gbs["ip"] = wx.GridBagSizer(0,0) row = 0 col = 0 sBmp = wx.StaticBitmap(self.panel["ip"], name="ip_sBmp") add2gbs(self.gbs["ip"], sBmp, (row,col), (1,1)) self.panel["ip"].SetSizer(self.gbs["ip"]) self.gbs["ip"].Layout() self.panel["ip"].SetupScrolling() ##### end of setting up input image panel ----- ##### beginning of setting up outpu image panel ----- self.gbs["op"] = wx.GridBagSizer(0,0) row = 0 col = 0 sBmp = wx.StaticBitmap(self.panel["op"], name="op_sBmp") add2gbs(self.gbs["op"], sBmp, (row,col), (1,1)) self.panel["op"].SetSizer(self.gbs["op"]) self.gbs["op"].Layout() self.panel["op"].SetupScrolling() ##### end of setting up output image panel ----- ### set up menu menuBar = wx.MenuBar() fileRenMenu = wx.Menu() selectFolders = fileRenMenu.Append( wx.Window.NewControlId(), item="Select folders\tCTRL+O", ) self.Bind(wx.EVT_MENU, lambda event: self.onButtonPressDown(event, 'selectFolders'), selectFolders) quit = fileRenMenu.Append( wx.Window.NewControlId(), item="Quit\tCTRL+Q", ) menuBar.Append(fileRenMenu, "&ImageProcessor") self.SetMenuBar(menuBar) ### set up hot keys idSelFolders = wx.Window.NewControlId() idQuit = wx.Window.NewControlId() self.Bind(wx.EVT_MENU, lambda event: self.onButtonPressDown(event, 'selectFolders'), id=idSelFolders) self.Bind(wx.EVT_MENU, self.onClose, id=idQuit) accel_tbl = wx.AcceleratorTable([ (wx.ACCEL_CMD, ord('O'), idSelFolders), (wx.ACCEL_CMD, ord('Q'), idQuit), ]) self.SetAcceleratorTable(accel_tbl) ### set up status-bar self.statusbar = self.CreateStatusBar(1) self.sbBgCol = self.statusbar.GetBackgroundColour() self.timer["sbTimer"] = None updateFrameSize(self, wSz) self.Bind(wx.EVT_CLOSE, self.onClose)
def __init__(self): if DEBUG: print("CamRecFrame.__init__()") ### init frame w_pos = [0, 25] wg = wx.Display(0).GetGeometry() wSz = (wg[2], int(wg[3] * 0.9)) wx.Frame.__init__( self, None, -1, "pyCamRec v.%s" % (__version__), pos=tuple(w_pos), size=tuple(wSz), style=wx.DEFAULT_FRAME_STYLE ^ (wx.RESIZE_BORDER | wx.MAXIMIZE_BOX), ) self.SetBackgroundColour('#333333') ### set app icon self.tbIcon = wx.adv.TaskBarIcon(iconType=wx.adv.TBI_DOCK) icon = wx.Icon("icon.ico") self.tbIcon.SetIcon(icon) ##### [begin] class attributes ----- self.logFile = "pCR_log.txt" self.recFolder = "recordings" self.w_pos = w_pos # window position self.wSz = wSz # window size self.fonts = getWXFonts(initFontSz=8, numFonts=3) self.cIndices = getCamIdx(maxNCam=4) # indices of cams if self.cIndices == []: msg = "No usable cams is attached." wx.MessageBox(msg, 'Info', wx.OK | wx.ICON_INFORMATION) self.Destroy() pi = self.setPanelInfo() self.pi = pi # pnael information self.gbs = {} # for GridBagSizer self.panel = {} # panels self.timer = {} # timers self.cams = {} # Cam class instances self.th = [] # List of threads for each cam self.q2m = queue.Queue() # queue to get massage from a thread self.q2t = [] # list of queues to send massage to a thread for ci in self.cIndices: self.cams[ci] = Cam(self, ci, self.logFile) self.th.append(-1) self.q2t.append(queue.Queue()) self.oCIdx = [] # opened cam indices self.nCOnSide = 0 # number of cam images on one side # each cam's frame size for displaying dCSz = copy(pi["rp"]["sz"]) # frame size of a cam for displaying self.dispCSz = dCSz # numpy array for displaying cam images self.dispArr = np.zeros(shape=(dCSz[1], dCSz[0], 3), dtype=np.uint8) self.is_recording = False # whether it's currently recording or not self.rSTime = -1 # recording start time self.rDur_sTxt = None # for showing recording duration self.preview_sBmp = None # for showing preview of selected cam self.disp_sBmp = None # for showing recording view of cam(s) self.dispImgRefreshIntv = 50 # Interval to refresh the combined frame # images from each cam. Increase this interval, if the image lags # then eventually the app gets frozen after a while. ##### [end] class attributes ----- if not path.isdir(self.recFolder): # recording folder doesn't exist mkdir(self.recFolder) # make one if not path.isfile(self.logFile): # log file doesn't exist ### make header in log file logHeader = "Timestamp, Message\n" logHeader += "#-------------------------------------------------\n" writeFile(self.logFile, logHeader) # write header ### create panels for pk in pi.keys(): self.panel[pk] = SPanel.ScrolledPanel( self, name="%s_panel" % (pk), pos=pi[pk]["pos"], size=pi[pk]["sz"], style=pi[pk]["style"], ) self.panel[pk].SetBackgroundColour(pi[pk]["bgCol"]) ##### beginning of setting up UI panel interface ----- bw = 5 # border width for GridBagSizer nCol = 4 # number columns uiSz = pi["ui"]["sz"] hlSz = (int(uiSz[0] * 0.95), -1) # size of horizontal line separator self.gbs["ui"] = wx.GridBagSizer(0, 0) row = 0 col = 0 sTxt = setupStaticText( self.panel["ui"], "Cam index: ", font=self.fonts[2], ) add2gbs(self.gbs["ui"], sTxt, (row, col), (1, 1)) col += 1 _choices = [str(x) for x in self.cIndices] _choices.insert(0, '') cho = wx.Choice( self.panel["ui"], -1, name="camIdx_cho", choices=_choices, ) cho.Bind(wx.EVT_CHOICE, self.onChoice) cho.SetSelection(0) add2gbs(self.gbs["ui"], cho, (row, col), (1, 1)) row += 1 col = 0 sTxt = setupStaticText( self.panel["ui"], "Initial frame image:", font=self.fonts[2], ) add2gbs(self.gbs["ui"], sTxt, (row, col), (1, nCol)) row += 1 col = 0 ### set up staticBitmap for preview w = int(uiSz[0] * 0.95) h = int(w / 1.333) sBmp = wx.StaticBitmap(self.panel["ui"], -1, size=(w, h)) img = wx.Image(w, h) img.SetData(np.zeros((h, w, 3), dtype=np.uint8).tostring()) sBmp.SetBitmap(img.ConvertToBitmap()) self.preview_sBmp = sBmp add2gbs(self.gbs["ui"], sBmp, (row, col), (1, nCol)) row += 1 col = 0 sTxt = setupStaticText( self.panel["ui"], "Output format: ", font=self.fonts[2], ) add2gbs(self.gbs["ui"], sTxt, (row, col), (1, 1)) col += 1 cho = wx.Choice( self.panel["ui"], -1, name="outputFormat_cho", choices=['video', 'image'], ) cho.Bind(wx.EVT_CHOICE, self.onChoice) cho.SetSelection(0) add2gbs(self.gbs["ui"], cho, (row, col), (1, 1)) row += 1 col = 0 sTxt = setupStaticText( self.panel["ui"], "Video FPS upper limit: ", font=self.fonts[2], ) add2gbs(self.gbs["ui"], sTxt, (row, col), (1, 2)) col += 2 spin = wx.SpinCtrl( self.panel["ui"], -1, size=(75, -1), min=1, max=60, initial=15, name='videoFPSlimit_spin', style=wx.SP_ARROW_KEYS | wx.SP_WRAP, ) add2gbs(self.gbs["ui"], spin, (row, col), (1, 1)) row += 1 col = 0 sTxt = setupStaticText( self.panel["ui"], "Image capture interval (seconds): ", font=self.fonts[2], ) add2gbs(self.gbs["ui"], sTxt, (row, col), (1, 2)) col += 2 spin = wx.SpinCtrlDouble( self.panel["ui"], -1, size=(75, -1), min=0.04, # min; 25 fps max=3600, initial=0.5, inc=1, # increment name='ssIntv_spin', style=wx.SP_ARROW_KEYS | wx.SP_WRAP, ) spin.Disable() add2gbs(self.gbs["ui"], spin, (row, col), (1, 1)) row += 1 col = 0 btn = wx.Button( self.panel["ui"], -1, label="Add", name="addCam_btn", size=(int(uiSz[0] * 0.463), -1), ) btn.Bind(wx.EVT_LEFT_DOWN, self.onButtonPressDown) btn.Disable() add2gbs(self.gbs["ui"], btn, (row, col), (1, 1)) col += 1 btn = wx.Button( self.panel["ui"], -1, label="Remove", name="remCam_btn", size=(int(uiSz[0] * 0.463), -1), ) btn.Bind(wx.EVT_LEFT_DOWN, self.onButtonPressDown) btn.Disable() add2gbs(self.gbs["ui"], btn, (row, col), (1, 2)) row += 1 col = 0 btn = wx.Button( self.panel["ui"], -1, label="Remove all cams", name="remAllCam_btn", size=(int(uiSz[0] * 0.95), -1), ) btn.Bind(wx.EVT_LEFT_DOWN, self.onButtonPressDown) add2gbs(self.gbs["ui"], btn, (row, col), (1, nCol)) row += 1 col = 0 sTxt = setupStaticText( self.panel["ui"], "Cams to record (Cam index [output-format(/FPS-limit when video or /interval when image)]):", font=self.fonts[1], wrapWidth=int(uiSz[0] * 0.95), ) add2gbs(self.gbs["ui"], sTxt, (row, col), (1, nCol)) row += 1 col = 0 sTxt = setupStaticText( self.panel["ui"], "[]", font=self.fonts[1], name="openCI_sTxt", ) add2gbs(self.gbs["ui"], sTxt, (row, col), (1, nCol)) row += 1 col = 0 add2gbs(self.gbs["ui"], wx.StaticLine(self.panel["ui"], -1, size=hlSz, style=wx.LI_HORIZONTAL), (row, col), (1, nCol)) # horizontal line separator row += 1 col = 0 btn = wx.Button( self.panel["ui"], -1, label="start Recording", name="toggleRec_btn", size=(int(uiSz[0] * 0.95), -1), ) btn.Bind(wx.EVT_LEFT_DOWN, self.onButtonPressDown) btn.Disable() add2gbs(self.gbs["ui"], btn, (row, col), (1, nCol)) row += 1 col = 0 sTxt = setupStaticText( self.panel["ui"], "Recording duration: ", font=self.fonts[2], ) add2gbs(self.gbs["ui"], sTxt, (row, col), (1, 1)) col += 1 sTxt = setupStaticText( self.panel["ui"], "0:00:00", font=self.fonts[2], fgColor="#ccccff", bgColor="#000000", ) self.rDur_sTxt = sTxt add2gbs(self.gbs["ui"], sTxt, (row, col), (1, nCol - 1)) self.panel["ui"].SetSizer(self.gbs["ui"]) self.gbs["ui"].Layout() self.panel["ui"].SetupScrolling() ##### end of setting up UI panel interface ----- ##### beginning of setting up recording panel interface ----- bw = 5 # border width for GridBagSizer self.gbs["rp"] = wx.GridBagSizer(0, 0) row = 0 col = 0 sBmp = wx.StaticBitmap(self.panel["rp"], -1, size=pi["rp"]["sz"]) self.disp_sBmp = sBmp self.panel["rp"].SetSizer(self.gbs["rp"]) self.gbs["rp"].Layout() self.panel["rp"].SetupScrolling() ##### end of setting up recording panel interface ----- ### set up menu menuBar = wx.MenuBar() fileRenMenu = wx.Menu() quit = fileRenMenu.Append( wx.Window.NewControlId(), item="Quit\tCTRL+Q", ) menuBar.Append(fileRenMenu, "&pyCamRec") self.SetMenuBar(menuBar) ### set up hot keys idQuit = wx.Window.NewControlId() self.Bind(wx.EVT_MENU, self.onClose, id=idQuit) accel_tbl = wx.AcceleratorTable([ (wx.ACCEL_CMD, ord('Q'), idQuit), ]) self.SetAcceleratorTable(accel_tbl) ### set up status-bar self.statusbar = self.CreateStatusBar(1) self.sbBgCol = self.statusbar.GetBackgroundColour() self.timer["sbTimer"] = None updateFrameSize(self, wSz) self.Bind(wx.EVT_CLOSE, self.onClose)