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 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 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 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 __init__(self): if DEBUG: print("PyListenerFrame.__init__()") if path.isdir('log') == False: mkdir('log') if path.isdir('recordings') == False: mkdir('recordings') ##### beginning of class attributes ----- self.flag_showCMInCol = True # whether to show center-of-mass # in each column in spectrogram self.logFile = "log/log_%s.txt"%(get_time_stamp()[:-9]) # determine # log file name, [:-9] cut off hh_mm_ss from timestamp self.spBmp = None # spectrogram image (bmp) self.timers = {} # contain wxPython's timers for this class self.gbs = {} # dictionary of gridBagSizer self.chkB_comp = [] # checkboxes to enable comparison parameters w_pos = (0, 25) self.w_sz = [ wx.Display(0).GetGeometry()[2], wx.Display(0).GetGeometry()[3]-w_pos[1]-100 ] # initial window size self.fonts = getWXFonts() # get fonts pi = {} # top panel for buttons and some settings pi["bp"] = dict(pos=(0, 0), sz=(self.w_sz[0], 50), bgCol="#cccccc", style=wx.TAB_TRAVERSAL|wx.SUNKEN_BORDER) # panel for showing info for real-time sound. pi["ip_sp"] = dict(pos=(0, pi['bp']['sz'][1]), sz=(int(self.w_sz[0]/2), 150), bgCol="#999999", style=wx.TAB_TRAVERSAL|wx.SUNKEN_BORDER) # panel for showing info for template WAV sound. ipspSz = pi["ip_sp"]["sz"] ipspPos = pi["ip_sp"]["pos"] pi["ip_spT"] = dict(pos=(ipspSz[0]+10, ipspPos[1]), sz=(self.w_sz[0]-ipspSz[0]-10, ipspSz[1]), bgCol="#999999", style=wx.TAB_TRAVERSAL|wx.SUNKEN_BORDER) # real-time spectrogram panel pi["sp"] = dict(pos=(ipspPos[0], ipspPos[1]+ipspSz[1]), sz=(ipspSz[0], int(PLL.INPUT_FRAMES_PER_BLOCK/2)), bgCol="#000000", style=wx.TAB_TRAVERSAL|wx.SUNKEN_BORDER) ipsptPos = pi["ip_spT"]["pos"] ipsptSz = pi["ip_spT"]["sz"] # spectrogram panel for template pi["spT"] = dict(pos=(ipsptPos[0], ipsptPos[1]+ipsptSz[1]), sz=(ipsptSz[0], pi["sp"]["sz"][1]), bgCol="#000000", style=wx.TAB_TRAVERSAL|wx.SUNKEN_BORDER) self.pi = pi # store panel information self.panel = {} # dictionary to put panels ### init PyListener class self.pl = PLL.PyListener(self, self, self.logFile) if self.pl.devIdx == []: self.onClose(None) ##### end of class attributes ----- # numpy array for spectrogram of sound from mic. self.pl.spAD = np.zeros( (pi['sp']['sz'][1], pi['sp']['sz'][0]), dtype=np.uint8 ) # numpy array for spectrogram of template WAV self.pl.tSpAD= np.zeros( (pi['spT']['sz'][1], pi['spT']['sz'][0]), dtype=np.uint8 ) ### init frame wx.Frame.__init__(self, None, -1, "PyListener - v.%s"%(__version__), pos = w_pos, size = self.w_sz) self.SetBackgroundColour('#333333') self.updateFrameSize() ### set app icon self.tbIcon = wx.adv.TaskBarIcon(iconType=wx.adv.TBI_DOCK) icon = wx.Icon("icon.ico") self.tbIcon.SetIcon(icon) ### create (scroll) panels for pk in pi.keys(): if pk == 'sp': # spctrogram panels self.panel[pk] = PLSp.SpectrogramPanel(self, pi["sp"]["pos"], pi["sp"]["sz"], self.pl) else: 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 == 'spT': # template spectrogram panel self.panel[pk].Bind(wx.EVT_PAINT, self.onPaintSPT) ##### beginning of setting up top button panel interface ----- bw = 5 # border width for GridBagSizer ### generate buttons bpBtns = [ 'selectTemplateFolder', 'selectTemplateFile', 'startStopListening', ] bpBtnLabels = [ 'Template folder', 'Template File', 'Start/Stop', ] self.gbs["bp"] = wx.GridBagSizer(0,0) row = 0; col = -1 for i in range(len(bpBtns)): col += 1 bn = bpBtns[i] btnSz = (150, -1) btn = wx.Button( self.panel["bp"], -1, bpBtnLabels[i], name=bn, size=btnSz, ) btn.Bind(wx.EVT_LEFT_DOWN, self.onBPButtonPress) if bn == 'startStopListening': bn += '_blue' # change bn for image file name set_img_for_btn( imgPath="input/img_%s.png"%(bn), btn=btn ) self.gbs["bp"].Add( btn, pos=(row,col), span=(1,1), flag=wx.ALIGN_CENTER_VERTICAL|wx.ALL, border=bw, ) ### ----- vlSz = (-1, 20) # size of vertical line seprator col += 1 self.gbs["bp"].Add( wx.StaticLine( self.panel["bp"], -1, size=vlSz, style=wx.LI_VERTICAL, ), pos=(row,col), flag=wx.ALIGN_CENTER_VERTICAL|wx.ALL, border=bw, ) # vertical line separator col += 1 sTxt = setupStaticText( self.panel["bp"], 'Input device: ', font=self.fonts[2], ) self.gbs["bp"].Add( sTxt, pos=(row,col), flag=wx.ALIGN_CENTER_VERTICAL|wx.ALL, border=bw, ) col += 1 self.devNames_cho = wx.Choice( self.panel["bp"], -1, choices=self.pl.devNames, ) self.gbs["bp"].Add( self.devNames_cho, pos=(row,col), flag=wx.ALIGN_CENTER_VERTICAL|wx.ALL, border=bw, ) col += 1 self.gbs["bp"].Add( wx.StaticLine( self.panel["bp"], -1, size=vlSz, style=wx.LI_VERTICAL, ), pos=(row,col), flag=wx.ALIGN_CENTER_VERTICAL|wx.ALL, border=bw, ) # vertical line separator col += 1 sTxt = setupStaticText( self.panel["bp"], 'Auto-contrast level (1-100): (Template)', font=self.fonts[2], ) self.gbs["bp"].Add( sTxt, pos=(row,col), flag=wx.ALIGN_CENTER_VERTICAL|wx.ALL, border=bw, ) col += 1 spin = wx.SpinCtrl( self.panel["bp"], -1, size=(50,-1), min=1, max=100, initial=int(self.pl.acThrTol_templ), name='templACThrTol_spin', ) spin.Bind(wx.EVT_SPINCTRL, self.onChangeACthrTolerance) self.gbs["bp"].Add( spin, pos=(row,col), flag=wx.ALIGN_CENTER_VERTICAL|wx.ALL, border=bw, ) col += 1 sTxt = setupStaticText( self.panel["bp"], ' (Mic.)', font=self.fonts[2], ) self.gbs["bp"].Add( sTxt, pos=(row,col), flag=wx.ALIGN_CENTER_VERTICAL|wx.ALL, border=bw, ) col += 1 spin = wx.SpinCtrl( self.panel["bp"], -1, size=(50,-1), min=1, max=100, initial=int(self.pl.acThrTol_nt), name='micACThrTol_spin', ) spin.Bind(wx.EVT_SPINCTRL, self.onChangeACthrTolerance) self.gbs["bp"].Add( spin, pos=(row,col), flag=wx.ALIGN_CENTER_VERTICAL|wx.ALL, border=bw, ) col += 1 self.gbs["bp"].Add( wx.StaticLine( self.panel["bp"], -1, size=vlSz, style=wx.LI_VERTICAL, ), pos=(row,col), flag=wx.ALIGN_CENTER_VERTICAL|wx.ALL, border=bw, ) # vertical line separator col += 1 chkB = wx.CheckBox( self.panel["bp"], -1, "Show CM in cols", style=wx.CHK_2STATE ) chkB.SetValue(True) chkB.Bind(wx.EVT_CHECKBOX, self.onChangeShowCMinCol) self.gbs["bp"].Add( chkB, pos=(row,col), flag=wx.ALIGN_CENTER_VERTICAL|wx.ALL, border=bw, ) ### ----- self.panel["bp"].SetSizer(self.gbs["bp"]) self.gbs["bp"].Layout() self.panel["bp"].SetupScrolling() ##### end of setting up top button panel interface ----- ##### beginning of setting up info panel ##### for mic. streaming spectrogram ----- bw = 5 self.gbs["ip_sp"] = wx.GridBagSizer(0,0) ### ----- row = 0; col = 0 self.txtSFInfo = wx.TextCtrl( self.panel["ip_sp"], -1, "", size=(ipspSz[0]-20, int(ipspSz[1]*0.9)), style=wx.TE_READONLY|wx.TE_MULTILINE, ) self.txtSFInfo.SetBackgroundColour((200,200,200)) self.gbs["ip_sp"].Add( self.txtSFInfo, pos=(row,col), span=(1,1), flag=wx.ALIGN_CENTER_VERTICAL|wx.ALL, border=bw, ) ### ----- self.panel["ip_sp"].SetSizer(self.gbs["ip_sp"]) self.gbs["ip_sp"].Layout() self.panel["ip_sp"].SetupScrolling() ##### end of setting up info panel ##### for mic. streaming spectrogram ----- ##### beginning of setting up info panel ##### for spectrogram of template WAV ----- bw = 5 self.gbs["ip_spT"] = wx.GridBagSizer(0,0) ### ----- row = 0; col = 0 sTxt = setupStaticText( self.panel["ip_spT"], "Folder for template", font=self.fonts[2], ) self.gbs["ip_spT"].Add( sTxt, pos=(row,col), span=(1,2), flag=wx.ALIGN_CENTER_VERTICAL|wx.ALL, border=bw, ) col += 2 txt = wx.TextCtrl( self.panel["ip_spT"], -1, "", name="comp_fName", size=(250, -1), style=wx.TE_READONLY ) txt.SetBackgroundColour((200,200,200)) self.gbs["ip_spT"].Add( txt, pos=(row,col), span=(1,3), flag=wx.ALIGN_CENTER_VERTICAL|wx.ALL, border=bw, ) ### ----- row += 1; col = 0 chkB = wx.CheckBox( self.panel["ip_spT"], -1, "Apply All/No thresholds", name="comp_applyAllNone_chk", style=wx.CHK_2STATE, ) chkB.SetValue(True) chkB.Bind(wx.EVT_CHECKBOX, self.onChecked_allNoneChkBox) self.gbs["ip_spT"].Add( chkB, pos=(row,col), span=(1,5), flag=wx.ALIGN_CENTER_VERTICAL|wx.ALL, border=bw, ) ### ----- row += 1; col = 0 sTxt = setupStaticText( self.panel["ip_spT"], "Apply", font=self.fonts[2], ) self.gbs["ip_spT"].Add( sTxt, pos=(row,col), flag=wx.ALIGN_CENTER_VERTICAL|wx.ALL, border=bw, ) col += 1 sTxt = setupStaticText( self.panel["ip_spT"], "Parameter", font=self.fonts[2], ) self.gbs["ip_spT"].Add( sTxt, pos=(row,col), flag=wx.ALIGN_CENTER_VERTICAL|wx.ALL, border=bw, ) col += 1 sTxt = setupStaticText( self.panel["ip_spT"], "Min. Val.", font=self.fonts[2], ) self.gbs["ip_spT"].Add( sTxt, pos=(row,col), flag=wx.ALIGN_CENTER_VERTICAL|wx.ALL, border=bw, ) col += 1 sTxt = setupStaticText( self.panel["ip_spT"], "Value", font=self.fonts[2], ) self.gbs["ip_spT"].Add( sTxt, pos=(row,col), flag=wx.ALIGN_CENTER_VERTICAL|wx.ALL, border=bw, ) col += 1 sTxt = setupStaticText( self.panel["ip_spT"], "Max. Val.", font=self.fonts[2], ) self.gbs["ip_spT"].Add( sTxt, pos=(row,col), flag=wx.ALIGN_CENTER_VERTICAL|wx.ALL, border=bw, ) ### Make TextCtrl objects ### for accepting range of thresholds for parameters ----- for key in self.pl.compParamList: if key in self.pl.indCPL: continue row += 1; col = 0 self.setupTemplParamWidgets( self.panel["ip_spT"], self.gbs["ip_spT"], row, key, self.pl.compParamLabel[key], bw ) row += 1; col = 0 sLine = wx.StaticLine( self.panel["ip_spT"], -1, size=(pi["spT"]["sz"][0]-50, -1), style=wx.LI_HORIZONTAL ) self.gbs["ip_spT"].Add( sLine, pos=(row,col), span=(1,5), flag=wx.ALIGN_CENTER_VERTICAL|wx.ALL, border=bw, ) for key in self.pl.indCPL: if key not in self.pl.compParamLabel: continue row += 1; col = 0 self.setupTemplParamWidgets( self.panel["ip_spT"], self.gbs["ip_spT"], row, key, self.pl.compParamLabel[key], bw, ) ### ----- self.panel["ip_spT"].SetSizer(self.gbs["ip_spT"]) self.gbs["ip_spT"].Layout() self.panel["ip_spT"].SetupScrolling() ##### end of setting up info panel ##### for spectrogram of template WAV ----- ### set up menu menuBar = wx.MenuBar() pyListenerMenu = wx.Menu() selectTemplate = pyListenerMenu.Append( wx.Window.NewControlId(), item="Select folder for template\tCTRL+O", ) self.Bind(wx.EVT_MENU, self.selectTemplate, selectTemplate) selectTemplateFile = pyListenerMenu.Append( wx.Window.NewControlId(), item="Select a file for template\tALT+O", ) self.Bind(wx.EVT_MENU, lambda event: self.selectTemplate('File'), selectTemplateFile) startStopListening = pyListenerMenu.Append( wx.Window.NewControlId(), item="Start/Stop listening\tSPACE", ) self.Bind(wx.EVT_MENU, lambda event: self.onBPButtonPress('startStopListening'), startStopListening) quit = pyListenerMenu.Append( wx.Window.NewControlId(), item="Quit\tCTRL+Q", ) self.Bind(wx.EVT_MENU, self.onClose, quit) menuBar.Append(pyListenerMenu, "&pyListener") self.SetMenuBar(menuBar) ### set up hot keys idSTFolder = wx.Window.NewControlId() idSTFile = wx.Window.NewControlId() idListen = wx.Window.NewControlId() idQuit = wx.Window.NewControlId() self.Bind(wx.EVT_MENU, self.selectTemplate, id=idSTFolder) self.Bind(wx.EVT_MENU, lambda event: self.selectTemplate('File'), id=idSTFile) self.Bind(wx.EVT_MENU, lambda event: self.onBPButtonPress('startStopListening'), id=idListen) self.Bind(wx.EVT_MENU, self.onClose, id=idQuit) accel_tbl = wx.AcceleratorTable([ (wx.ACCEL_CMD, ord('O'), idSTFolder), (wx.ACCEL_ALT, ord('O'), idSTFile), (wx.ACCEL_NORMAL, wx.WXK_SPACE, idListen), (wx.ACCEL_CMD, ord('Q'), idQuit), ]) self.SetAcceleratorTable(accel_tbl) # set up status-bar #self.statusbar = self.CreateStatusBar(1) #self.sbTimer = None self.Bind(wx.EVT_CLOSE, self.onClose)
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