Ejemplo n.º 1
0
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]]
Ejemplo n.º 2
0
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'))
Ejemplo n.º 3
0
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
Ejemplo n.º 4
0
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)