def getFrameScoreCurrent(self): ''' return the score or None. ''' try: objScoring = ScoreSchema() objScoring.load(self.getFrameNoteCurrent().get('scoring', None)) return objScoring.getAll() except: return None
def test_ScoreSchema_addObj_number_type(): # make sure all data gets converted to int-type # addCircle inp_data = [11.0, 12, float(15), float(0)] ss = ScoreSchema() ss.addCircle(inp_data, objEnum=0) ret_data = ss.getAll()['0']['data'] assert all(map(lambda elem: type(elem) is int, ret_data)) assert ret_data == [11, 12, 15, 0] # addRay inp_data = [[11.0, 12.9999], [float(15), float(0.1)]] ss = ScoreSchema() ss.addRay(inp_data, objEnum=0) ret_data = ss.getAll()['0']['data'] assert all(map(lambda elem: type(elem) is int, ret_data[0])) assert all(map(lambda elem: type(elem) is int, ret_data[1])) assert ret_data == [[11, 12], [15, 0]]
def test_ScoreSchema_load_number_type(): # test that we coerce all data into int # circle inp_data = {'0': {'type': 'circle', 'data': [1.1, 2, float(15), 0.9999]}} ss = ScoreSchema() ss.load(inp_data) ret_data = ss.getAll()['0']['data'] assert ret_data == [1, 2, 15, 0] assert all(map(lambda elem: type(elem) is int, ret_data)) # ray inp_data = {'0': {'type': 'ray', 'data': [[1.1, 2], [float(15), 0.9999]]}} ss = ScoreSchema() ss.load(inp_data) ret_data = ss.getAll()['0']['data'] assert ret_data == [[1, 2], [15, 0]] assert all(map(lambda elem: type(elem) is int, ret_data[0])) assert all(map(lambda elem: type(elem) is int, ret_data[1]))
def test_fromScalarsAddObject(): ss = ScoreSchema() assert ss.getAll() is None fields = { 'objtype': 'circle', 'data0': 100, 'data1': 100, 'data2': 40, 'data3': 50, 'misc': 'blah balh' } ss.fromScalarsAddObject(objEnum=0, **fields) assert ss.getAll() == {'0': {'type': 'circle', 'data': [100, 100, 40, 50]}} # test overwrite of objEnum=0 ss.fromScalarsAddObject(objEnum=0, **fields) assert ss.getAll() == {'0': {'type': 'circle', 'data': [100, 100, 40, 50]}} # test multiple obj's fields2 = { 'objtype': 'circle', 'data0': 200, 'data1': 100, 'data2': 30, 'data3': 20, 'misc': 'blah balh' } ss.fromScalarsAddObject(objEnum=3, **fields2) assert ss.getAll() == { '0': { 'type': 'circle', 'data': [100, 100, 40, 50] }, '3': { 'type': 'circle', 'data': [200, 100, 30, 20] } }
class NotesFactory: ''' handle non-timelog data associated with each video, and and each frame ''' def __init__(self): self.metalog = MetaDataLog() self.vidIsLoaded = False self.isProcessed = False self.dataVid = {} self.frameNoteFailed = False self.orientation = 0 self.compression = -1 self.bOverideFramenote = False self.framesDataExisting = [] self.frameInd = None self.displayFrameScoring = ScoreSchema() self.frameLogInputPathFn = None self.defaultLogFrameInputPathFn = "notes/guiview.jsonc" self.defaultFrameNotePathFn = "notes/framenote.json" self.defaultFrameNoteOveridePathFn = "notes/framenote-override.jsonc" def setFrameLogInput(self, frameLogPathFn): ''' set the path to framelog input which is json template for creation of frameData-dict''' if frameLogPathFn == "": self.frameLogInputPathFn = self.defaultLogFrameInputPathFn else: self.frameLogInputPathFn = frameLogPathFn try: _f = open(self.frameLogInputPathFn, 'r') _f.close() except: print 'couldnt open frameLogInput at: ', frameLogPathFn self.frameLogInputPathFn = None def setFrameCurrent(self, frameInd): self.frameInd = frameInd def setDisplayScoring(self, scoringData): self.displayFrameScoring.load(scoringData) # if scoringData is None, displayFrameScoring get's reset def getOrientation(self): return self.orientation #degrees clockwise def getCompression(self): if self.compression == 0: # this is the code for lossless return 1 return 0 def getBallColor(self): try: return self.dataVid['notes']['details']['ball_color'] except: return None def setCmd(self, switchOverideNote=None): if switchOverideNote is not None: self.bOverideFramenote = switchOverideNote # note: still need to read this below in # output.setCmd, don't alter global here. def loadMetaLog(self, metalogPathFn): try: self.dataVid = self.metalog.get_log_data(metalogPathFn) self.orientation = self.dataVid.get('notes', 0).get('orientation', 0) self.compression = self.dataVid.get('fourcc_enum', -1) if isinstance(self.metalog.data, dict): if len(self.metalog.data.keys()) > 0: self.vidIsLoaded = True if self.dataVid.get('processed', False): self.isProcessed = True self.loadFrameNotes() else: self.dataVid['processed'] = True self.dataVid['proc-data'] = {} #e.g. datetime of processing except: self.dataVid = {} def getBaseMetaLog(self): fullLog = copy.deepcopy(self.dataVid) try: del fullLog["frames"] except: pass return fullLog def loadFrameNotes(self): ''' if video is processed it already has frame notes, load those ''' try: assert len(self.dataVid['frames']) > 0 assert isinstance(self.dataVid['frames'][0], dict) self.framesDataExisting = self.dataVid['frames'] except: self.framesDataExisting = [] def getFrameNoteCurrent(self): ''' return a dict representing frame data ''' try: return self.framesDataExisting[self.frameInd] except: return {} def getFrameScoreCurrent(self): ''' return the score or None. ''' try: objScoring = ScoreSchema() objScoring.load(self.getFrameNoteCurrent().get('scoring', None)) return objScoring.getAll() except: return None def getFrameScoreCurrentDict(self): ''' return the score or empty dict; like a wrapper for getFrameScoreCurrent''' result = self.getFrameScoreCurrent() if result is None: return {} return result def checkFrameHasScore(self): ''' return True is there's anything stored in framenote.scoring ''' return self.getFrameNoteCurrent().get('scoring', None) is not None def getFrameType(self): try: frameNote = self.getFrameNoteCurrent() return frameNote['frame_type'] except: return None def getFrameScoreForTrack(self): ''' returns relevant params from metalog to tracking module ''' try: return self.getFrameType(), self.getFrameScoreCurrent() except: return None, None def loadFrameLogCurrent(self): ''' load data from notepad ''' if self.frameLogInputPathFn is not None: try: with open(self.frameLogInputPathFn, "r") as f: lines = f.readlines() input_str = ''.join(lines) #remove comments input_str = re.sub(r'\\\n', '', input_str) input_str = re.sub(r'//.*\n', '\n', input_str) return json.loads(input_str) except: print 'couldnt parse framelog jsonc' return {} else: return {} def outputFrameNote(self): ''' write out current frame note into a text file; edit if needed''' if not (self.isProcessed): return try: with open(self.defaultFrameNotePathFn, 'w') as f: json.dump(self.getFrameNoteCurrent(), f, indent=4) self.frameNoteFailed = False except: self.frameNoteFailed = True def loadFrameNoteInput(self): ''' read in frame note input, this will absorb any edits you made''' if not (self.isProcessed): return try: with open(self.defaultFrameNotePathFn, 'r') as f: lines = f.readlines() lines = "".join(lines) frameNote = json.loads(lines) return frameNote except: return self.getFrameNoteCurrent() def loadFramenoteOveride(self): if not (self.isProcessed): return try: with open(self.defaultFrameNoteOveridePathFn, 'r') as f: lines = f.readlines() lines = "".join(lines) lines = re.sub(r'\\\n', '', lines) lines = re.sub(r'//.*\n', '\n', lines) return json.loads(lines) except: print 'failed to load ', str(self.defaultFrameNoteOveridePathFn) return {} def getFrameData(self): ''' Return frameData. If no existing data, create new frameData-dict. If frameData already exists and was loaded; there are multiple opportunites to update/add/delete the existing data here, based on gui-cmd's and editing files in an editor. Data is ultimately consumed by output. ''' if self.isProcessed: if not (self.frameNoteFailed): #override any param for this frame in notepad frameData = self.loadFrameNoteInput() else: #couldnt find/open txt file; keep the same frameData = self.getFrameNoteCurrent() if self.displayFrameScoring.checkHasContents(): #gui-cmd: writeFrame+Score - update/add-to scoring dict #note: if gui-cmd is not called, displayFrameScoring has no contents frameData['scoring'] = self.mergeDicts( main=self.getFrameScoreCurrentDict(), update=self.displayFrameScoring.getAll(), b_add=True) if self.bOverideFramenote: #gui-cmd: writeFrame+Override - add/overwrite params from notepad frameData = self.mergeDicts(main=frameData, update=self.loadFramenoteOveride(), b_add=False) else: #frameData is new; load the framedata from txt file frameData = self.loadFrameLogCurrent() frameData['orig_vid_index'] = self.frameInd frameData['scoring'] = self.displayFrameScoring.getAll() return frameData def getBaseNote(self): baseNote = copy.deepcopy(self.dataVid) try: del baseNote['frames'] except: pass #some metalogs don't have frames assert 'frames' not in baseNote.keys() return baseNote def getBaseFrameNote(self, frameNote): baseFrameNote = copy.deepcopy(frameNote) try: del baseFrameNote['scoring'] except: pass #some frameNotes don't have scoring assert 'scoring' not in baseFrameNote.keys() return baseFrameNote #TODO - refactor to separate module @classmethod def mergeDicts(cls, main, update, b_add=False): ''' with terminal nodes in update(dict) overwrite the value at those nodes in main(dict) if they exist, or add them if b_add=True: b_add=False: main: {"a": 1, "b": 2, "inner": {"z":1}} update: {"b":99, "inner":{"z": -99}, "x":-55} -> {"a": 1, "b": 99, "inner":{"z": -99}} b_add=True: main: {"a": 1, "b": 2, "inner": {"z":1}} update: {"b":99, "inner":{"z": -99}, "x":-55} -> {"a": 1, "b": 99, "inner":{"z": -99}, "x":-55} (output should have updates and (possibly) adds but never deletes) ''' try: assert isinstance(main, dict) assert isinstance(update, dict) except Exception as e: print e.message return main updateKeys, updateVals = cls.recurseKeys(update) newMain = cls.recurseUpdate(copy.deepcopy(main), updateKeys, updateVals) if b_add: newMain = cls.recurseAdd(copy.deepcopy(main), updateKeys, updateVals) return newMain @classmethod def recurseKeys(cls, inputDict): ''' build [nested] list for keys and vals in inputDict: inputDict: {"a":1, "b":2, "c": {"c_a":17}} ->: ["a", "b", ["c", "c_a"]], [1, [None, 17], 2]) ''' keyList, valList = [], [] for k in inputDict.keys(): if isinstance(inputDict[k], dict): tmpKey, tmpVal = cls.recurseKeys(inputDict[k]) nestedKey = [k] nestedVal = [None] if len(tmpKey) > 0: nestedKey.extend(tmpKey) nestedVal.extend(tmpVal) keyList.append(nestedKey) valList.append(nestedVal) else: keyList.append(k) valList.append(inputDict[k]) return keyList, valList @classmethod def recurseUpdate(cls, inputDict, listKeys, listVals): ''' only update if key is in inputDict ''' if inputDict is None: return None for _key, _val in zip(listKeys, listVals): if isinstance(_key, list): _dict = cls.recurseUpdate( copy.deepcopy(inputDict.get(_key[0], None)), _key[1:], _val[1:]) inputDict[_key[0]] = _dict else: if inputDict.has_key(_key): inputDict[_key] = _val else: # main doesn't have that key pass return inputDict @classmethod def recurseAdd(cls, inputDict, listKeys, listVals): if inputDict is None: inputDict = {} for _key, _val in zip(listKeys, listVals): if isinstance(_key, list): _dict = cls.recurseAdd( copy.deepcopy(inputDict.get(_key[0], None)), _key[1:], _val[1:]) inputDict[_key[0]] = _dict else: inputDict[_key] = _val return inputDict
class TrackFactory: ''' handles tracking processing and tracking-stored-data ''' def __init__(self, on=False): self.on = on self.currentFrame = None self.currentFrameInd = -1 self.trackingOnPrevious = None self.bTrackingOnChange = False self.currentTrackSuccess = False self.currentTrackScore = ScoreSchema() self.threshInitial = [(0, 0, 0), (255, 255, 255)] self.threshes = [] self.lastFrame = None self.declaredBallColor = "" self.bPerformTrainOnNewData = False self.trainingData = [] self.trainingThreshes = [] self.bTrackTimer = False self.trackTimerData = {} self.savedParams = None # tp_: tracking parameters self.tp_trackAlgoEnum = 0 self.tp_tracking_blur = 1 self.tp_repair_iterations = 1 self.tp_b_hsv = False # TrackingAlgo class inherits TrackingTemplate # and is instantiated here? def setInit(self, ballColor=""): self.declaredBallColor = ballColor if self.declaredBallColor == "green": self.threshInitial = [(29, 86, 6), (64, 255, 255)] self.threshes.append(tuple(self.threshInitial)) self.threshes.append(((20, 60, 6), (40, 255, 255))) if self.declaredBallColor == "orange": thresh1 = ((6, 30, 120), (64, 255, 255)) thresh2 = ((64, 100, 180), (90, 255, 255)) thresh3 = ((90, 120, 200), (120, 255, 255)) self.threshInitial = thresh1 self.threshes = [thresh1, thresh2, thresh3] def setAlgoEnum(self, algoEnum): self.tp_trackAlgoEnum = algoEnum def setCmd(self, trackingOn, outputParams=None, alterParams=None, resetParams=None): self.on = trackingOn # this is for forcing a display "redraw" when track toggles On/Off if self.trackingOnPrevious is not None: if trackingOn != self.trackingOnPrevious: self.bTrackingOnChange = True else: self.bTrackingOnChange = False self.trackingOnPrevious = trackingOn if outputParams is not None: if outputParams: self.outputParams() g.switchOutputParams = False if alterParams is not None: if alterParams: self.saveParams() self.alterParams() g.switchAlterParams = False self.bTrackingOnChange = True if resetParams is not None: if resetParams: self.restParams() g.switchResetParams = False self.bTrackingOnChange = True def outputParams(self): try: with open("notes/tracking_params.json", "w") as f: json.dump(self.getTrackParams(), f, indent=4) except: print 'failed to output tracking params' def alterParams(self): try: with open("notes/tracking_params.json", "r") as f: d_params = json.load(f) except Exception as e: print 'failed to read in json' print e.message try: self.setTrackParams(**d_params) except Exception as e: print 'failed to set track params from json' print e def saveParams(self): self.savedParams = self.getTrackParams() def restParams(self): self.setTrackParams(**self.savedParams) def resetTracker(self): self.trackTimerData = {} def getTrackOnChange(self): ''' return True to make a pass thru inner loop of guiview only updating state related to tracking. This allows us to make changes immediately apparent in Display''' if self.bTrackingOnChange is None: return False else: return self.bTrackingOnChange def getTrackScore(self): if not (self.on): return None return self.currentTrackScore.getAll() def setFrameInd(self, frameInd): self.currentFrameInd = frameInd def setFrame(self, currentFrame): if not (self.on): return self.currentFrame = currentFrame def setFrameScore(self, frameScoreData): if not (self.on): return if frameScoreData is None: return if len(frameScoreData) != 2: return frameType, frameScore = frameScoreData objScoring = ScoreSchema() objScoring.load(frameScore) circleDataObj0 = objScoring.getData(objEnum=0) if frameType == "training": datum = self.buildTrainingDatum(circleDataObj0, self.currentFrame) if datum is not None: self.trainingData.append(datum) if self.bPerformTrainOnNewData: self.trainProc() if frameType == "scoring": pass #do evaluation @classmethod def buildTrainingDatum(cls, cropRect, img): if cropRect is None or img is None: return None try: datum = {} datum['cropRect'] = cropRect datum['cropImg'] = crop_img(img.copy(), cls.absRect(cropRect)) return datum except: return None def trainProc(self): ''' take training data and build thresh hi/lo from them ''' if len(self.trainingData) < 1: return # only run iterthresh on latest img img = self.trainingData[len(self.trainingData) - 1].get( 'cropImg', None) if img is None: return if img.shape[0] < 1 or img.shape[1] < 1: return circle_img = pixlist_to_pseduoimg(filter_pixels_circle(img)) out_thresh = iterThreshA(circle_img, goal_pct=.95, steep=False) _lo, _hi = out_thresh[1][3], out_thresh[1][4] self.trainingThreshes.append((_lo, _hi)) self.combine_threshes(self.trainingThreshes) self.threshInitial = (tuple(map(int, _lo)), tuple(map(int, _hi))) #TODO - update self.threshes, if necessary def setTrackTimer(self, bTrackTimer): if isinstance(bTrackTimer, bool): self.bTrackTimer = bTrackTimer def getTrackTimerData(self): return self.trackTimerData def getTrackTimerDataCurrent(self): return self.trackTimerData.get(self.currentFrameInd - 1, -1) def setTrackParams(self, tracking_blur=None, repair_iterations=None, thresh_lo=None, thresh_hi=None, threshes=None): if tracking_blur is not None: self.tp_tracking_blur = tracking_blur if repair_iterations is not None: self.tp_repair_iterations = repair_iterations if thresh_lo is not None: self.threshInitial[0] = tuple(thresh_lo) if thresh_hi is not None: self.threshInitial[1] = tuple(thresh_hi) if threshes is not None: self.threshes = copy.copy(threshes) def getTrackParams(self): params = {} params['tracking_blur'] = self.tp_tracking_blur params['repair_iterations'] = self.tp_repair_iterations params['thresh_lo'] = self.threshInitial[0] params['thresh_hi'] = self.threshInitial[1] params['threshes'] = copy.copy(self.threshes) return params def trackFrame(self, b_log=False): ''' Wrapper function for a particular trackAlgo: - unpack parameters - select the particular track algo to run - [possibly] time the function (if self.bTrackTimer) - [possibly] return log of img transform steps (if b_log) questions/todos: [ ] pass in objEnum to trackAlgo for multi-obj tracking ''' if not (self.on): return tracking_blur = self.tp_tracking_blur repair_iterations = self.tp_repair_iterations thresh_lo = self.threshInitial[0] thresh_hi = self.threshInitial[1] threshes = copy.copy(self.threshes) last_frame = self.lastFrame if self.bTrackTimer: t0 = time.time() ret = None if self.tp_trackAlgoEnum == 0: ret = self.trackDefault( tracking_blur=tracking_blur, repair_iterations=repair_iterations, thresh_lo=thresh_lo, thresh_hi=thresh_hi, b_log=b_log, objEnum=0 #TODO-SS ) elif self.tp_trackAlgoEnum == 1: ret = self.trackDemoNew(tracking_blur=tracking_blur, repair_iterations=repair_iterations, thresh_lo=thresh_lo, thresh_hi=thresh_hi, b_log=b_log # ,objEnum = 0 ) elif self.tp_trackAlgoEnum == 2: ret = self.trackMultiThresh1(tracking_blur=tracking_blur, repair_iterations=repair_iterations, threshes=threshes, b_log=b_log # ,objEnum = 0 ) elif self.tp_trackAlgoEnum == 3: ret = self.trackDummyRichLog(tracking_blur=tracking_blur, repair_iterations=repair_iterations, threshes=threshes, b_log=b_log, last_frame=last_frame # ,objEnum = 0 ) else: print 'trackAlgoEnum not recognized' if self.bTrackTimer: if self.currentFrameInd not in self.trackTimerData.keys(): t_proc = time.time() - t0 self.trackTimerData[self.currentFrameInd] = t_proc if ret is not None: return ret # tracker algos -------- def trackDefault(self, tracking_blur, repair_iterations, thresh_lo, thresh_hi, b_log=False, objEnum=0): ''' trackDefault: Template for writing a track algo. - add documentation notes, describing how this algo is different; in this case we're describing the templating. - organize all parameters used in func args; these are retreived from the instance in parent function, trackFrame, and thus read-only. - add b_log and objEnum defaults to args - write trackAlgo output data to instance properties: self.currentTrackSuccess self.currentTrackScore (as a DataSchema.ScoreSchema) - return None, unless b_log - in which case, include a b_log section: - add all possible transforms to 'keys' list. - set 'data' for each possible key, mimicing control flow for early return in the function (this will be used to debug in notebooks, but is not necessary for most purposes.) questions / todos: [ ] will we overwrite data before it hits b_log return data? ''' img_t = transformA(self.currentFrame.copy(), tracking_blur) img_mask = threshA(img_t, threshLo=thresh_lo, threshHi=thresh_hi) if not (img_mask is None) and (img_mask.sum() != 0): img_mask_2 = repairA(img_mask, iterations=repair_iterations) x, y = find_xy(img_mask_2) radius = find_radius(img_mask_2) if radius > 0: self.currentTrackSuccess = True self.currentTrackScore.addCircle(self.circleToRect( (x, y, radius)), objEnum=objEnum) else: self.currentTrackSuccess = False self.currentTrackScore.reset() else: self.currentTrackSuccess = False self.currentTrackScore.reset() if b_log: keys = [ 'img_t', 'img_mask', 'img_repair', 'xy', 'radius', 'scoreCircle' ] data = OrderedDict() for k in keys: data[k] = None data['img_t'] = img_t data['img_mask'] = img_mask if img_mask is not None: data['img_repair'] = img_mask_2 data['xy'] = (x, y) data['radius'] = radius data['scoreCircle'] = self.currentTrackScore.getObjRect(0) return data def trackDemoNew(self, tracking_blur, repair_iterations, thresh_lo, thresh_hi, b_log=False): ''' trackDemoNew: Example for adding a non-default trackAlgo. Only a few small changes to this function: - allow us to bypass repairA step by setting repair_iterations to 0 - early return when img_mask is blank questions / todos: [x] verify early return skips a section and verify the notebook workflow process handles the missing data gracefully in mutliPlot() [ ] is a tracking_blur = 1 do any changes? [ ] does repair_iteration = 0 work in trackDefault()? ''' img_t = transformA(self.currentFrame.copy(), tracking_blur) img_mask = threshA(img_t, threshLo=thresh_lo, threshHi=thresh_hi) if img_mask.sum() != 0: if repair_iterations > 0: img_repair = repairA(img_mask, iterations=repair_iterations) img_terminal = img_repair else: img_terminal = img_mask x, y = find_xy(img_terminal) radius = find_radius(img_terminal) if radius > 0: self.currentTrackSuccess = True self.currentTrackScore.addCircle(self.circleToRect( (x, y, radius)), objEnum=0) else: self.currentTrackSuccess = False self.currentTrackScore.reset() else: self.currentTrackSuccess = False self.currentTrackScore.reset() if b_log: keys = [ 'img_t', 'img_mask', 'img_repair', 'img_terminal', 'xy', 'radius', 'scoreCircle' ] data = OrderedDict() for k in keys: data[k] = None data['img_t'] = img_t data['img_mask'] = img_mask if img_mask.sum() != 0: data['img_terminal'] = img_terminal data['xy'] = (x, y) data['radius'] = radius if repair_iterations > 0: data['img_repair'] = img_repair if radius > 0: data['scoreCircle'] = self.currentTrackScore.getObjRect(0) return data def trackMultiThresh1(self, tracking_blur, repair_iterations, threshes, b_log=False): ''' trackMultiThresh1: Evolved from trackDemoNew; uses multiple thresh intervals questions / todos: [ ] multiple threshes ''' img_t = transformA(self.currentFrame.copy(), tracking_blur) img_mask = threshMultiOr(img_t, threshes=threshes) if img_mask.sum() != 0: if repair_iterations > 0: img_repair = repairA(img_mask, iterations=repair_iterations) img_terminal = img_repair else: img_terminal = img_mask x, y = find_xy(img_terminal) radius = find_radius(img_terminal) if radius > 0: self.currentTrackSuccess = True self.currentTrackScore.addCircle(self.circleToRect( (x, y, radius)), objEnum=0) else: self.currentTrackSuccess = False self.currentTrackScore.reset() else: self.currentTrackSuccess = False self.currentTrackScore.reset() if b_log: keys = [ 'img_t', 'img_mask', 'img_repair', 'img_terminal', 'xy', 'radius', 'scoreCircle' ] data = OrderedDict() for k in keys: data[k] = None data['img_t'] = img_t data['img_mask'] = img_mask if img_mask.sum() != 0: data['img_terminal'] = img_terminal data['xy'] = (x, y) data['radius'] = radius if repair_iterations > 0: data['img_repair'] = img_repair if radius > 0: data['scoreCircle'] = self.currentTrackScore.getObjRect(0) return data def trackDummyRichLog(self, tracking_blur, repair_iterations, threshes, last_frame, b_log=False): ''' trackDummyRichLog: copied from trackMultiThresh1; here we're just adding extra track_log keys like img_diff and img_diff_repair to see how they'll impact reporting functions downstream. this is the start though of looking at adding a movement element to trackers and we're adding the last_frame input / self.lastFrame output. ''' img_t = transformA(self.currentFrame.copy(), tracking_blur) img_mask = threshMultiOr(img_t, threshes=threshes) # add new-lines in here: if last_frame is not None and False: img_diff = cv2.absdiff(self.currentFrame, last_frame) if repair_iterations > 0: img_diff_repair = repairA(img_diff, iterations=repair_iterations) self.lastFrame = self.currentFrame.copy() if img_mask.sum() != 0: if repair_iterations > 0: img_repair = repairA(img_mask, iterations=repair_iterations) img_terminal = img_repair else: img_terminal = img_mask x, y = find_xy(img_terminal) radius = find_radius(img_terminal) if radius > 0: self.currentTrackSuccess = True self.currentTrackScore.addCircle(self.circleToRect( (x, y, radius)), objEnum=0) else: self.currentTrackSuccess = False self.currentTrackScore.reset() else: self.currentTrackSuccess = False self.currentTrackScore.reset() if b_log: keys = [ 'img_t', 'img_mask', 'img_repair', 'img_dummy', 'img_dummy_2', 'img_diff', 'img_diff_repair' 'img_terminal', 'img_terminal_2' 'xy', 'radius', 'scoreCircle' ] data = OrderedDict() for k in keys: data[k] = None data['img_t'] = img_t data['img_mask'] = img_mask data['img_dummy'] = transformA(self.currentFrame.copy(), 11) data['img_dummy_2'] = transformA(self.currentFrame.copy(), 5) if last_frame is not None and False: data['img_diff'] = img_diff data['img_diff_repair'] = img_diff_repair if img_mask.sum() != 0: data['img_terminal'] = img_terminal data['img_terminal_2'] = img_terminal data['xy'] = (x, y) data['radius'] = radius if repair_iterations > 0: data['img_repair'] = img_repair if radius > 0: data['scoreCircle'] = self.currentTrackScore.getObjRect(0) return data # helper functions ------ @staticmethod def absRect(input_rect): ''' takes an (opencv style) relative rect, returns an absolute rect. (x0,y0, d_x, d_y) -> ((xo,y0),(x1, y1)) note: must be tuples, not lists; to use in opencv functions ''' x = copy.copy(input_rect) rect = ((int(x[0]), int(x[1])), (int(x[0] + x[2]), int(x[1] + x[3]))) return rect @staticmethod def circleToRect(input_circle): ''' takes x,y, radius, fits to enclosing relative-format rect (x,y, radius) -> (x0,y0, d_x, d_y) ''' x, y, radius = copy.copy(input_circle) x0 = int(x - radius) y0 = int(y - radius) dx = int(2 * radius) dy = int(2 * radius) return (x0, y0, dx, dy) @staticmethod def combine_threshes(data, liberal=True): ''' data is a list of (lo, hi) 3-ple's; find the union ''' if len(data) < 1: return (np.array([0, 0, 0], dtype='uint8'), np.array([255, 255, 255], dtype='uint8')) _lo, _hi = [[255, 255, 255], [0, 0, 0]] for row in data: lo, hi = row[0], row[1] for i, clr in enumerate(lo): if clr < _lo[i]: _lo[i] = clr for i, clr in enumerate(hi): if clr > _hi[i]: _hi[i] = clr return (np.array(_lo, dtype='uint8'), np.array(_hi, dtype='uint8'))
def show_scoring_on_off_1(input_test_child_dir, input_circle_data, b_rebench=False): ''' test that turning show_scoring on/off affects main display panel test that show_scoring=on + no scoring data is handled input params: - tests have dif size frames - tests have dif scoring-data ''' # setup ------ TEST_CHILD_DIR = input_test_child_dir stub_frame = cv2.imread( os.path.join(TEST_PARENT_DIR, TEST_CHILD_DIR, "stubframe.png")) bench_yes_scoring = cv2.imread( os.path.join(TEST_PARENT_DIR, TEST_CHILD_DIR, "bench_yes_score.png")) bench_no_scoring = cv2.imread( os.path.join(TEST_PARENT_DIR, TEST_CHILD_DIR, "bench_no_score.png")) some_scoring = ScoreSchema() some_scoring.addCircle(input_circle_data) stub_some_score = some_scoring.getAll() none_scoring = ScoreSchema() stub_none_score = none_scoring.getAll() diff = ImgDiff(log_path=DIFF_LOG_DIR) # run test ----- stage = StagingDisplay() stage.all_display_methods(b_showscoring=True, stub_frame=stub_frame.copy(), stub_scorecurrent=copy.deepcopy(stub_some_score)) scoring_on_output = stage.mock_get_frame() stage = StagingDisplay() stage.all_display_methods( b_showscoring=False #test-variable , stub_frame=stub_frame.copy(), stub_scorecurrent=copy.deepcopy(stub_some_score)) scoring_off_output = stage.mock_get_frame() stage = StagingDisplay() stage.all_display_methods( b_showscoring=True, stub_frame=stub_frame.copy(), stub_scorecurrent=copy.deepcopy(stub_none_score) #test-variable ) scoring_none_output = stage.mock_get_frame() #rebench --- if b_rebench: if verifyAction(prefix="\nrebench:" + input_test_child_dir): return cv2.imwrite( os.path.join(TEST_PARENT_DIR, TEST_CHILD_DIR, "bench_yes_score.png"), scoring_on_output) cv2.imwrite( os.path.join(TEST_PARENT_DIR, TEST_CHILD_DIR, "bench_no_score.png"), scoring_off_output) return # verify ---- assert diff.diffImgs(bench_yes_scoring, scoring_on_output) assert diff.diffImgs(bench_no_scoring, scoring_off_output) assert diff.diffImgs(bench_no_scoring, scoring_none_output) assert diff.diffImgs(scoring_on_output, scoring_off_output, noLog=True) == False
def scoring_annotate_obj(input_test_child_dir, b_rebench=False): ''' test that we annotate score objects in main_display note: we can't initialize with annotateObjEnum=True or we get unhandled err input params: - different scores (stored as json file in test dir) ''' # setup ------ TEST_CHILD_DIR = input_test_child_dir stub_frame = cv2.imread( os.path.join(TEST_PARENT_DIR, TEST_CHILD_DIR, "stubframe.png")) bench_main = cv2.imread( os.path.join(TEST_PARENT_DIR, TEST_CHILD_DIR, "bench_main.png")) bench_score = cv2.imread( os.path.join(TEST_PARENT_DIR, TEST_CHILD_DIR, "bench_score.png")) bench_naframe = cv2.imread( os.path.join(TEST_PARENT_DIR, TEST_CHILD_DIR, "naframe.png")) stub_score_obj = ScoreSchema() with open(os.path.join(TEST_PARENT_DIR, TEST_CHILD_DIR, "stubscore.json"), "r") as f: stub_score_obj.load(json.load(f)) stub_score = stub_score_obj.getAll() diff = ImgDiff(log_path=DIFF_LOG_DIR) # run test ----- stage = StagingDisplay() stage.some_display_methods( p_all=True, stub_frame=stub_frame.copy(), stub_scorecurrent=copy.deepcopy(stub_score), b_showscoring=True, annotateObjEnum=False #note ) stage.some_display_methods(p_all_byframe=True, p_input=True, stub_frame=stub_frame.copy(), stub_scorecurrent=copy.deepcopy(stub_score), b_showscoring=True, annotateObjEnum=True) main1 = stage.mock_get_frame() score1 = stage.mock_get_score_frame() #rebench --- if b_rebench: if verifyAction(prefix="\nrebench:" + input_test_child_dir): return cv2.imwrite( os.path.join(TEST_PARENT_DIR, TEST_CHILD_DIR, "bench_main.png"), main1) cv2.imwrite( os.path.join(TEST_PARENT_DIR, TEST_CHILD_DIR, "bench_score.png"), score1) return # verify ---- assert diff.diffImgs(bench_main, main1) assert diff.diffImgs(bench_score, score1)
def scoring_obj_enum(input_test_child_dir, input_obj_enum, b_wrong=False, b_rebench=False): ''' test that showscoring with properly formed is drawn to main_display and score_display input params: - different scores (stored as json file in test dir) - different scoringenum's to focus on with score_display - b_wrong: if True, then there is no score with that input_obj_enum, thus no score_display ''' # setup ------ TEST_CHILD_DIR = input_test_child_dir stub_frame = cv2.imread( os.path.join(TEST_PARENT_DIR, TEST_CHILD_DIR, "stubframe.png")) bench_main = cv2.imread( os.path.join(TEST_PARENT_DIR, TEST_CHILD_DIR, "bench_main.png")) bench_score = cv2.imread( os.path.join(TEST_PARENT_DIR, TEST_CHILD_DIR, "bench_score.png")) bench_naframe = cv2.imread( os.path.join(TEST_PARENT_DIR, TEST_CHILD_DIR, "naframe.png")) stub_score_obj = ScoreSchema() with open(os.path.join(TEST_PARENT_DIR, TEST_CHILD_DIR, "stubscore.json"), "r") as f: stub_score_obj.load(json.load(f)) stub_score = stub_score_obj.getAll() diff = ImgDiff(log_path=DIFF_LOG_DIR) # run test ----- stage = StagingDisplay() stage.all_display_methods( stub_frame=stub_frame.copy(), stub_scorecurrent=copy.deepcopy(stub_score), b_showscoring=True, i_scoringenum=input_obj_enum #test-variable ) main1 = stage.mock_get_frame() score1 = stage.mock_get_score_frame() #rebench --- if b_rebench: if verifyAction(prefix="\nrebench:" + input_test_child_dir): return cv2.imwrite( os.path.join(TEST_PARENT_DIR, TEST_CHILD_DIR, "bench_main.png"), main1) if b_wrong: cv2.imwrite( os.path.join(TEST_PARENT_DIR, TEST_CHILD_DIR, "naframe.png"), score1) else: cv2.imwrite( os.path.join(TEST_PARENT_DIR, TEST_CHILD_DIR, "bench_score.png"), score1) return # verify ---- assert diff.diffImgs(bench_main, main1) if b_wrong: assert diff.diffImgs(bench_naframe, score1) else: assert diff.diffImgs(bench_score, score1)
def show_tracking_on_off_1(input_test_child_dir, input_circle_data, b_rebench=False): ''' test that turning tracking on/off affecta main_display and that it affects score_display input params: - tests have dif size frames - tests have dif scoring-data TODO: [x] modulo zero resize ''' # setup ------ TEST_CHILD_DIR = input_test_child_dir stub_frame = cv2.imread( os.path.join(TEST_PARENT_DIR, TEST_CHILD_DIR, "stubframe.png")) bench_yes_tracking = cv2.imread( os.path.join(TEST_PARENT_DIR, TEST_CHILD_DIR, "bench_yes_track_main.png")) bench_no_tracking = cv2.imread( os.path.join(TEST_PARENT_DIR, TEST_CHILD_DIR, "bench_no_track_main.png")) bench_score = cv2.imread( os.path.join(TEST_PARENT_DIR, TEST_CHILD_DIR, "bench_yes_track_score.png")) some_scoring = ScoreSchema() some_scoring.addCircle(input_circle_data) stub_some_score = some_scoring.getAll() none_scoring = ScoreSchema() stub_none_score = none_scoring.getAll() diff = ImgDiff(log_path=DIFF_LOG_DIR) # run test ----- #1: stage = StagingDisplay() stage.all_display_methods(stub_frame=stub_frame.copy(), stub_trackscore=copy.deepcopy(stub_some_score)) main1 = stage.mock_get_frame() score1 = stage.mock_get_score_frame() #2: stage = StagingDisplay() stage.all_display_methods( stub_frame=stub_frame.copy(), stub_trackscore=copy.deepcopy(stub_none_score) #test-variable ) main2 = stage.mock_get_frame() score2 = stage.mock_get_score_frame() #3: stage = StagingDisplay() stage.all_display_methods( b_showscoring=True #test-variable , stub_frame=stub_frame.copy(), stub_trackscore=copy.deepcopy(stub_some_score)) main3 = stage.mock_get_frame() score3 = stage.mock_get_score_frame() #rebench --- if b_rebench: if verifyAction(prefix="\nrebench:" + input_test_child_dir): return cv2.imwrite( os.path.join(TEST_PARENT_DIR, TEST_CHILD_DIR, "bench_yes_track_main.png"), main1) cv2.imwrite( os.path.join(TEST_PARENT_DIR, TEST_CHILD_DIR, "bench_no_track_main.png"), main2) cv2.imwrite( os.path.join(TEST_PARENT_DIR, TEST_CHILD_DIR, "bench_yes_track_score.png"), score3) return # verify ---- assert diff.diffImgs(bench_yes_tracking, main1) assert diff.diffImgs(bench_no_tracking, main2) assert diff.diffImgs(bench_yes_tracking, main3) assert diff.diffImgs(main1, main2, noLog=True) == False assert diff.diffImgs(bench_score, score1) assert score2 is None assert diff.diffImgs(bench_score, score3)