class StruckTracker(GenericVisionTracker): """ TODO """ def __init__(self): """ Initialize the tracker """ self.name = 'struck_tracker' f = open("config.txt", "w") f.write(struck_config) f.close() self.reset() def reset(self): """ Reset the tracker :return: """ self._primed = False self.bbox = None def _prime(self, im, bounding_region): """ prime tracker on image and bounding box :param im: input image (3 - channel numpy array) :type im: numpy.ndarray :param bounding_region: initial bounding region of the tracked object :type bounding_region: PVM_tools.bounding_region.BoundingRegion """ self.bbox=bounding_region bounding_box = bounding_region.get_box_pixels() struck.STRUCK_init(im, bounding_box) def _track(self, im): """ Track on given image, return a bounding box :param im: image (3 - channel numpy array) :type im: numpy.ndarray :return: bounding box of the tracker object :rtype: PVM_tools.bounding_region.BoundingRegion """ # this is required so that tracker is re-initialized if it is primed again self._primed = False struck.STRUCK_track(im) struck_bbox = struck.STRUCK_get_bbox() self.bounding_box = [struck_bbox["xmin"], struck_bbox["ymin"], struck_bbox["width"], struck_bbox["height"]] if self.bounding_box == (): self.bbox = BoundingRegion() else: self.bbox = BoundingRegion(image_shape=(im.shape[0], im.shape[1], 3), box=self.bounding_box) return self.bbox.copy() def get_heatmap(self, heatmap_name=None): return self.bbox.get_mask()
class CenterVisionTracker(GenericVisionTracker): """ This class exposes the null vision tracker which just always returns its priming bounding box """ def __init__(self, size_factor=0.2, new_name=None): """ Initialize the tracker """ if new_name is None: self.name = 'center_tracker' else: self.name = new_name self.size_factor = size_factor def reset(self): """ Reset the tracker :return: """ self._primed = False self.bbox = None def _prime(self, im, bounding_region): """ prime tracker on image and bounding box :param im: input image (3 - channel numpy array) :type im: numpy.ndarray :param bounding_region: initial bounding region of the tracked object :type bounding_region: PVM_tools.bounding_region.BoundingRegion """ box = np.array([(im.shape[1] - im.shape[1] * self.size_factor) / 2, (im.shape[0] - im.shape[0] * self.size_factor) / 2, im.shape[1] * self.size_factor, im.shape[0] * self.size_factor]).astype(np.int) self.bbox = BoundingRegion(image_shape=im.shape, box=box) if not self._primed: self._primed = True def _track(self, im): """ Track on given image, rseturn a bounding box :param im: image (3 - channel numpy array) :type im: numpy.ndarray :return: bounding box of the tracker object :rtype: PVM_tools.bounding_region.BoundingRegion """ # this is required so that tracker is re-initialized if it is primed again self._primed = False return self.bbox.copy() def get_heatmap(self, heatmap_name=None): return self.bbox.get_mask()
class CenterVisionTracker(GenericVisionTracker): """ This class exposes the null vision tracker which just always returns its priming bounding box """ def __init__(self, size_factor=0.2, new_name=None): """ Initialize the tracker """ if new_name is None: self.name = 'center_tracker' else: self.name = new_name self.size_factor = size_factor def reset(self): """ Reset the tracker :return: """ self._primed = False self.bbox = None def _prime(self, im, bounding_region): """ prime tracker on image and bounding box :param im: input image (3 - channel numpy array) :type im: numpy.ndarray :param bounding_region: initial bounding region of the tracked object :type bounding_region: PVM_tools.bounding_region.BoundingRegion """ box = np.array([(im.shape[1]-im.shape[1]*self.size_factor)/2, (im.shape[0]-im.shape[0]*self.size_factor)/2, im.shape[1]*self.size_factor, im.shape[0]*self.size_factor]).astype(np.int) self.bbox = BoundingRegion(image_shape=im.shape, box=box) if not self._primed: self._primed = True def _track(self, im): """ Track on given image, rseturn a bounding box :param im: image (3 - channel numpy array) :type im: numpy.ndarray :return: bounding box of the tracker object :rtype: PVM_tools.bounding_region.BoundingRegion """ # this is required so that tracker is re-initialized if it is primed again self._primed = False return self.bbox.copy() def get_heatmap(self, heatmap_name=None): return self.bbox.get_mask()
class TLDVisionTracker(GenericVisionTracker): """ This class exposes the open tld tracker implemented in C++ by http://www.gnebehay.com/tld/. Originally OpenTLD that was originally published in MATLAB by Zdenek Kalal. """ def __init__(self): """ Initialize the tracker """ self.name = 'tld_tracker' self.reset() def reset(self): """ Reset the tracker :return: """ self.tld = None self._primed = False self.bbox = None def _prime(self, im, bounding_region): """ prime tracker on image and bounding box :param im: input image (3 - channel numpy array) :type im: numpy.ndarray :param bounding_region: initial bounding region of the tracked object :type bounding_region: PVM_tools.bounding_region.BoundingRegion """ self.bbox = bounding_region bounding_box = bounding_region.get_box_pixels() if not self._primed: self.tld = tld.TLD2() self._primed = True self.height, self.width = im.shape[:2] self.tld.set_width_and_height((self.width, self.height)) im = cv2.cvtColor(im, cv2.COLOR_RGB2GRAY) img_cvmat = cv2.cv.fromarray(im) if bounding_box[0] + bounding_box[2] > self.width: bounding_box[2] = self.width - bounding_box[0] if bounding_box[1] + bounding_box[3] > self.height: bounding_box[3] = self.height - bounding_box[1] self.tld.selectObject( img_cvmat, tuple([ int(bounding_box[0]), int(bounding_box[1]), int(bounding_box[2]), int(bounding_box[3]) ])) def _track(self, im): """ Track on given image, return a bounding box :param im: image (3 - channel numpy array) :type im: numpy.ndarray :return: bounding box of the tracker object :rtype: PVM_tools.bounding_region.BoundingRegion """ # this is required so that tracker is re-initialized if it is primed again self._primed = False img_cvmat = cv2.cv.fromarray(im) self.tld.processImage(img_cvmat) self.bounding_box = self.tld.getCurrBB() self.confidence = self.tld.currConf if self.bounding_box == (): self.bbox = BoundingRegion() else: self.bbox = BoundingRegion(image_shape=(im.shape[0], im.shape[1], 3), box=self.bounding_box, confidence=self.confidence) return self.bbox.copy() def get_heatmap(self, heatmap_name=None): return self.bbox.get_mask()
class CMTVisionTracker(GenericVisionTracker): """ This class exposes the CMT tracker implemented in http://www.gnebehay.com/CMT/. """ def __init__(self): """ Initialize the tracker """ self.name = 'cmt_tracker' self.reset() def reset(self): """ Reset the tracker :return: """ self.cmt = None self._primed = False self.bbox = None def _prime(self, im, bounding_region): """ prime tracker on image and bounding box :param im: input image (3 - channel numpy array) :type im: numpy.ndarray :param bounding_region: initial bounding region of the tracked object :type bounding_region: PVM_tools.bounding_region.BoundingRegion """ self.bbox=bounding_region bounding_box = bounding_region.get_box_pixels() if not self._primed: this_dir = os.path.dirname(os.path.realpath(__file__)) sys.path.append(this_dir+"/original_cmt/") from CMT import CMT self.cmt = CMT() self._primed = True self.height, self.width = im.shape[:2] im = cv2.cvtColor(im, cv2.COLOR_RGB2GRAY) self.cmt.initialise(im_gray0=im, tl=tuple([bounding_box[0], bounding_box[1]]), br=tuple([bounding_box[0]+bounding_box[2], bounding_box[1]+bounding_box[3]])) self.im_prev = im def _track(self, im): """ Track on given image, return a bounding box :param im: image (3 - channel numpy array) :type im: numpy.ndarray :return: bounding box of the tracker object :rtype: PVM_tools.bounding_region.BoundingRegion """ # this is required so that tracker is re-initialized if it is primed again self._primed = False self.im_prev = im im = cv2.cvtColor(im, cv2.COLOR_RGB2GRAY) self.cmt.process_frame(im) self.confidence = 1 if np.isnan(self.cmt.bb).any(): self.bbox = BoundingRegion() else: self.bbox = BoundingRegion(image_shape=im.shape, box=self.cmt.bb, confidence=self.confidence) return self.bbox.copy() def get_heatmap(self, heatmap_name=None): return self.bbox.get_mask()
class StruckTracker(GenericVisionTracker): """ TODO """ def __init__(self): """ Initialize the tracker """ self.name = 'struck_tracker' f = open("config.txt", "w") f.write(struck_config) f.close() self.reset() def reset(self): """ Reset the tracker :return: """ self._primed = False self.bbox = None def _prime(self, im, bounding_region): """ prime tracker on image and bounding box :param im: input image (3 - channel numpy array) :type im: numpy.ndarray :param bounding_region: initial bounding region of the tracked object :type bounding_region: PVM_tools.bounding_region.BoundingRegion """ self.bbox = bounding_region bounding_box = bounding_region.get_box_pixels() struck.STRUCK_init(im, bounding_box) def _track(self, im): """ Track on given image, return a bounding box :param im: image (3 - channel numpy array) :type im: numpy.ndarray :return: bounding box of the tracker object :rtype: PVM_tools.bounding_region.BoundingRegion """ # this is required so that tracker is re-initialized if it is primed again self._primed = False struck.STRUCK_track(im) struck_bbox = struck.STRUCK_get_bbox() self.bounding_box = [ struck_bbox["xmin"], struck_bbox["ymin"], struck_bbox["width"], struck_bbox["height"] ] if self.bounding_box == (): self.bbox = BoundingRegion() else: self.bbox = BoundingRegion(image_shape=(im.shape[0], im.shape[1], 3), box=self.bounding_box) return self.bbox.copy() def get_heatmap(self, heatmap_name=None): return self.bbox.get_mask()
class PVMVisionTracker(GenericVisionTracker): """ This class exposes the null vision tracker which just always returns its priming bounding box """ def __init__(self, filename="", remote_filename="", cores="4", storage=None, steps_per_frame=1): """ Initialize the tracker """ self.name = 'PVMtracker' if filename == "": filename = storage.get(remote_filename) self.prop_dict = CoreUtils.load_model(filename) logging.info("Loaded the dictionary %s", filename) PVM_Create.upgrade_dictionary_to_ver1_0(self.prop_dict) self.prop_dict['num_proc'] = int(cores) for k in range(len(self.prop_dict['learning_rates'])): self.prop_dict['learning_rates'][k][0] = 0.0 logging.info("Setting learning rate in layer %d to zero" % k) self.prop_dict["readout_learning_rate"][0] = 0.0 logging.info("Setting readout learning rate to zero") self.manager = Manager(self.prop_dict, 1000) self.executor = CoreUtils.ModelExecution(prop_dict=self.prop_dict, manager=self.manager, port=9100) self.executor.start(blocking=False) self.threshold = 32 self.image_size = self.prop_dict['input_array'].shape[:2][::-1] self.readout_heatmap = np.zeros(self.image_size, dtype=np.float) self.step_per_frame = steps_per_frame def reset(self): """ Reset the tracker :return: """ self._primed = False self.bbox = None def _prime(self, im, bounding_region): """ prime tracker on image and bounding box :param im: input image (3 - channel numpy array) :type im: numpy.ndarray :param bounding_region: initial bounding region of the tracked object :type bounding_region: PVM_tools.bounding_region.BoundingRegion """ if not self._primed: logging.info("Priming the PVM tracker") logging.info("Setting up the tracker threshold to %d" % self.threshold) self._primed = True def _track(self, im): """ Track on given image, rseturn a bounding box :param im: image (3 - channel numpy array) :type im: numpy.ndarray :return: bounding box of the tracker object :rtype: PVM_tools.bounding_region.BoundingRegion """ # this is required so that tracker is re-initialized if it is primed again current_frame = cv2.resize(im, dsize=self.image_size) self.prop_dict['input_array'][:] = current_frame self.prop_dict['input_array_float'][:] = current_frame.astype( np.float) / 255 for i in range(self.step_per_frame): self.executor.step() norm = 1.0 / len(self.prop_dict['predicted_readout_arrays']) self.readout_heatmap[:] = 0 for k in self.prop_dict['predicted_readout_arrays']: self.readout_heatmap[:] += cv2.resize(k.view(np.ndarray), dsize=self.image_size) * norm am = np.unravel_index(np.argmax(self.readout_heatmap), self.readout_heatmap.shape) if len(am) == 3: for d in range(3): if am[2] != d: self.readout_heatmap[:, :, d] = 0 self.heatmap = self.readout_heatmap.view(np.ndarray) if len(self.heatmap.shape) == 3: self.heatmap = np.max(self.heatmap, axis=2) self.heatmap = cv2.resize(self.heatmap, dsize=(im.shape[1], im.shape[0]), interpolation=cv2.INTER_CUBIC) self.heatmap = (self.heatmap * 255).astype(np.uint8) if np.max(self.heatmap) > (np.median(self.heatmap) + self.threshold): threshold = (np.max(self.heatmap) - np.median( self.heatmap)) * 0.5 + np.median(self.heatmap) ret, thresh = cv2.threshold(self.heatmap, threshold, 255, 0) contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE) if len(contours) > 0: max_cnt = None for cnt in contours: pt = np.unravel_index(np.argmax(self.heatmap), self.heatmap.shape) if cv2.pointPolygonTest(cnt, (pt[1], pt[0]), False) >= 0: max_cnt = cnt break if max_cnt is None: self.bbox = BoundingRegion() else: x, y, w, h = cv2.boundingRect(max_cnt) if w * h > 25: self.bbox = BoundingRegion(image_shape=im.shape, box=[x, y, w, h]) self.bbox.scale(1.1) else: self.bbox = BoundingRegion() else: self.bbox = BoundingRegion() else: self.bbox = BoundingRegion() self._primed = False return self.bbox.copy() def get_heatmap(self, heatmap_name=None): return self.heatmap() def finish(self): self.manager._running = False self.executor.step() self.executor.finish()
class TLDVisionTracker(GenericVisionTracker): """ This class exposes the open tld tracker implemented in C++ by http://www.gnebehay.com/tld/. Originally OpenTLD that was originally published in MATLAB by Zdenek Kalal. """ def __init__(self): """ Initialize the tracker """ self.name = 'tld_tracker' self.reset() def reset(self): """ Reset the tracker :return: """ self.tld = None self._primed = False self.bbox = None def _prime(self, im, bounding_region): """ prime tracker on image and bounding box :param im: input image (3 - channel numpy array) :type im: numpy.ndarray :param bounding_region: initial bounding region of the tracked object :type bounding_region: PVM_tools.bounding_region.BoundingRegion """ self.bbox=bounding_region bounding_box = bounding_region.get_box_pixels() if not self._primed: self.tld = tld.TLD2() self._primed = True self.height, self.width = im.shape[:2] self.tld.set_width_and_height((self.width, self.height)) im = cv2.cvtColor(im, cv2.COLOR_RGB2GRAY) img_cvmat = cv2.cv.fromarray(im) if bounding_box[0] + bounding_box[2] > self.width: bounding_box[2] = self.width - bounding_box[0] if bounding_box[1] + bounding_box[3] > self.height: bounding_box[3] = self.height - bounding_box[1] self.tld.selectObject(img_cvmat, tuple([int(bounding_box[0]), int(bounding_box[1]), int(bounding_box[2]), int(bounding_box[3])])) def _track(self, im): """ Track on given image, return a bounding box :param im: image (3 - channel numpy array) :type im: numpy.ndarray :return: bounding box of the tracker object :rtype: PVM_tools.bounding_region.BoundingRegion """ # this is required so that tracker is re-initialized if it is primed again self._primed = False img_cvmat = cv2.cv.fromarray(im) self.tld.processImage(img_cvmat) self.bounding_box = self.tld.getCurrBB() self.confidence = self.tld.currConf if self.bounding_box == (): self.bbox = BoundingRegion() else: self.bbox = BoundingRegion(image_shape=(im.shape[0], im.shape[1], 3), box=self.bounding_box, confidence=self.confidence) return self.bbox.copy() def get_heatmap(self, heatmap_name=None): return self.bbox.get_mask()
class LabelingApp(object): def __init__(self, filename, output=None, channel="default", target="default"): self.input_filename = filename if output is None: self.output_file = self.input_filename else: self.output_file = output self.channel = channel self.target = target self.reset() def reset(self, reload=False): cv2.destroyAllWindows() fc = FrameCollection() if reload: fc.load_from_file(filename=self.output_file) else: fc.load_from_file(filename=self.input_filename) self.movie = fc self.tracker = None self.current_frame_index = 0 self.right_button_pressed = False self.left_button_pressed = False self._anchor = None self.x_size = 30 self.y_size = 30 self._tracked_bounds = None self._stored_bounds = None self._current_bounds = BoundingRegion() # Make sure self.timer is set self.schedule_callback() # Initialization of the Graphic User Interface self.ready_to_refresh = threading.Lock() self.timer_lock = threading.Lock() self.win_name = 'Labeling video GUI' self.image = self.movie.Frame( self.current_frame_index).get_image(channel=self.channel) self.display_image = self.movie.Frame( self.current_frame_index).get_image(channel=self.channel) self.image_shape = self.image.shape self.create_windows() self.set_target_absent() self.image_buffer = {} self._last_update = time.time() self.refresh_timer = threading.Timer(0.05, self.refresh) self.refresh_timer.start() self.needs_refresh = True self.trim_end = len(self.movie) - 1 self.trim_start = 0 def fill_up_the_buffer(self): r0 = max(self.current_frame_index - 5, 0) r1 = min(self.current_frame_index + 5, len(self.movie)) for i in xrange(r0, r1, 1): if i not in self.image_buffer.keys(): self.image_buffer[i] = self.movie.Frame(i).get_image( channel=self.channel) for i in self.image_buffer.keys(): if i < r0 or i > r1: del self.image_buffer[i] def create_windows(self): ''' Create playback progress bar (allow user to move along the movie) ''' cv2.namedWindow(self.win_name) cv2.moveWindow(self.win_name, 150, 150) cv2.namedWindow("completeness") cv2.moveWindow("completeness", 150, 50) cv2.setMouseCallback(self.win_name, self.trackbar_window_onMouse) cv2.createTrackbar("Frame", self.win_name, 0, len(self.movie) - 1, self.jump_to_frame_callback) cv2.createTrackbar("Trim start", self.win_name, 0, len(self.movie) - 1, self.set_trim_start) cv2.createTrackbar("Trim end", self.win_name, len(self.movie) - 1, len(self.movie) - 1, self.set_trim_end) im_height = 30 # 30 pixels high completeness bar im_width = len(self.movie) self.completeness_image = np.zeros((im_height, im_width, 3)) current_frame_index = int(im_width * float(self.current_frame_index) / len(self.movie)) self.completeness_image[:, current_frame_index, :] = 1. self.completeness = np.zeros(im_width, dtype=np.uint8) for i in xrange(len(self.movie)): bounds = self.movie.Frame(i).get_label(channel=self.channel, target=self.target) if bounds is None or bounds.empty: if bounds is None: self.movie.Frame(i).set_label(channel=self.channel, target=self.target, label=BoundingRegion()) self.completeness[i] = 2 else: if bounds.is_keyframe(): self.completeness[i] = 1 else: self.completeness[i] = 0 def set_trim_start(self, frame_index): self.trim_start = frame_index + 0 def set_trim_end(self, frame_index): self.trim_end = frame_index + 0 def jump_to_frame_callback(self, frame_index): self.current_frame_index = frame_index self.image = self.movie.Frame( self.current_frame_index).get_image(channel=self.channel) self.needs_refresh = True def update_completeness_window(self): if self.ready_to_refresh.acquire(False): self.completeness_image *= 0 self.completeness_image[:, self.current_frame_index, :] = 255 for i in xrange(len(self.movie)): self.completeness_image[:, i, self.completeness[i]] = 255 cv2.putText(self.completeness_image, '%i' % self.current_frame_index, (10, 10), cv2.FONT_HERSHEY_PLAIN, 1.0, (255, 255, 255), lineType=cv2.CV_AA) cv2.imshow('completeness', self.completeness_image) self.ready_to_refresh.release() def update_trackbar_window(self): if self.ready_to_refresh.acquire(): image = self.image.copy() self._stored_bounds = self.movie.Frame( self.current_frame_index).get_label(channel=self.channel, target=self.target) # display rectangle over selected area self._current_bounds.draw_box(image, color=(255, 255, 255)) self._current_bounds.draw_box(image, color=(0, 0, 0), thickness=1) # display rectangle over stored area if self._stored_bounds is not None: self._stored_bounds.draw_box(image, color=(255, 0, 0)) if self._tracked_bounds is not None: self._tracked_bounds.draw_box(image, color=(0, 255, 0)) # reshape to display if too small min_width = 320 if image.shape[1] < min_width: resize_height = int(0.5 + image.shape[0] * min_width / float(image.shape[1])) image = cv2.resize(image, (min_width, resize_height), interpolation=cv2.INTER_NEAREST) self.display_image = image self.ready_to_refresh.release() def refresh(self): if self.timer_lock.acquire(): if self.needs_refresh: self.update_trackbar_window() self.update_completeness_window() self.needs_refresh = False self.refresh_timer = threading.Timer(0.05, self.refresh) self.refresh_timer.start() self.timer_lock.release() def trackbar_window_onMouse(self, event, x, y, flags, _): cv2.imshow(self.win_name, self.display_image) # Update state self.right_button_pressed = (flags & cv2.EVENT_FLAG_RBUTTON ) != 0 and event != cv2.EVENT_RBUTTONUP self.left_button_pressed = (flags & cv2.EVENT_FLAG_LBUTTON ) != 0 and event != cv2.EVENT_LBUTTONUP # Set bounding box if event == cv2.EVENT_MBUTTONDOWN: if self._anchor is None: # Setting the anchor self._anchor = (x, y) else: # Defining the box self.save_and_advance() elif self._anchor is not None and event != cv2.EVENT_MBUTTONUP: # We are creating new bounding box from anchor to current position self.x_size = max(5, int(abs(x - self._anchor[0]) + 0.5)) self.y_size = max(5, int(abs(y - self._anchor[1]) + 0.5)) center_x = (x + self._anchor[0]) / 2 center_y = (y + self._anchor[1]) / 2 self.set_bounding_box_with_center(center_x, center_y) elif event == cv2.EVENT_MBUTTONUP: self.x_size = max(5, int(abs(x - self._anchor[0]) + 0.5)) self.y_size = max(5, int(abs(y - self._anchor[1]) + 0.5)) center_x = (x + self._anchor[0]) / 2 center_y = (y + self._anchor[1]) / 2 self.set_bounding_box_with_center(center_x, center_y) keyframe = False if flags & cv2.EVENT_FLAG_CTRLKEY != 0: keyframe = True self.save_and_advance(keyframe=keyframe) else: self.set_bounding_box_with_center(x, y) # If this is a left/right button down event, # we can advance one frame immediately # and then start the timer if event == cv2.EVENT_RBUTTONDOWN: self.set_target_absent() self.save_and_advance() self.right_button_pressed = True if self.timer.finished: # Rearm the timer self.schedule_callback() elif event == cv2.EVENT_LBUTTONDOWN: self.set_bounding_box_with_center(x, y) keyframe = False if flags & cv2.EVENT_FLAG_CTRLKEY != 0: keyframe = True self.save_and_advance(keyframe=keyframe) self.left_button_pressed = True if self.timer.finished: # Rearm the timer self.schedule_callback() elif not (self.right_button_pressed or self.left_button_pressed): # No mouse button pressed, so cancel the timer if it's running self.timer.cancel() if time.time() - self._last_update > 0.1: self._last_update = time.time() self.needs_refresh = True def set_target_absent(self): self._current_bounds = BoundingRegion() def set_bounding_box_with_center(self, x, y): box = [ np.clip(int(x + 0.5) - self.x_size / 2, 0, self.image_shape[1]), np.clip(int(y + 0.5) - self.y_size / 2, 0, self.image_shape[0]), self.x_size + min(int(x + 0.5) - self.x_size / 2, 0), self.y_size + min(int(y + 0.5) - self.y_size / 2, 0) ] self._current_bounds = BoundingRegion(box=box) self.needs_refresh = True def schedule_callback(self): # 0.2 secs => 5 Hz self.timer = threading.Timer(0.1, self.timer_callback) self.timer.start() def timer_callback(self): if self.right_button_pressed or self.left_button_pressed: self.save_and_advance() self.schedule_callback() def advance_current_frame(self, increment=1): self.current_frame_index = min( len(self.movie) - 1, max(0, self.current_frame_index + increment)) self.image = self.movie.Frame( self.current_frame_index).get_image(channel=self.channel) cv2.setTrackbarPos("Frame", self.win_name, self.current_frame_index) if self.tracker is not None: self._tracked_bounds = self.tracker.track( self.movie.Frame( self.current_frame_index).get_image(channel=self.channel)) def save_and_advance(self, keyframe=False): self._anchor = None self.movie.Frame(self.current_frame_index).set_label( self._current_bounds.copy(), channel=self.channel, target=self.target) if self._current_bounds.empty: self.completeness[self.current_frame_index] = 2 else: if self._current_bounds.is_keyframe(): self.completeness[self.current_frame_index] = 1 else: self.completeness[self.current_frame_index] = 0 if keyframe: self.make_keyframe() self.advance_current_frame(15) else: self.advance_current_frame(1) def toggle_tracker(self): if self.tracker is None: self.tracker = CMTVisionTracker() self.tracker.prime( self.movie.Frame( self.current_frame_index).get_image(channel=self.channel), self._current_bounds) logging.warning("Tracker is enabled") else: self.tracker = None self._tracked_bounds = None logging.warning("Tracker is disabled") def advance_tracker(self): if self._tracked_bounds is not None: self._current_bounds = self._tracked_bounds self.save_and_advance() def export_movie(self): trim_frames_front = self.trim_start trim_frames_end = (len(self.movie) - 1) - self.trim_end if trim_frames_end > 0: for i in range(trim_frames_end): self.movie.delete(-1) if trim_frames_front > 0: for i in range(trim_frames_front): self.movie.delete(0) logging.warning("Exporting result in file " + self.output_file) self.movie.write_to_file(self.output_file) logging.warning("Exporting completed !") def interpolate(self, start, end): print "Interpolating %d %d" % (start, end) label0 = self.movie.Frame(start).get_label(channel=self.channel, target=self.target) label1 = self.movie.Frame(end).get_label(channel=self.channel, target=self.target) if (label0.empty) or (label1.empty): return box0 = label0.get_box_pixels() box1 = label1.get_box_pixels() for i in xrange(start + 1, end, 1): alpha = (end - i) * 1.0 / (end - start) box2 = alpha * np.array(box0) + (1 - alpha) * np.array(box1) box2 = map(lambda x: int(x), box2) self.movie.Frame(i).set_label(BoundingRegion( box=box2, image_shape=self.movie.Frame(i).get_image( channel=self.channel).shape), channel=self.channel, target=self.target) self.completeness[i] = 0 def make_keyframe(self): self.completeness[self.current_frame_index] = 1 self.movie.Frame(self.current_frame_index).get_label( channel=self.channel, target=self.target).set_keyframe(True) # find previous keyframe next_keyframe = -1 for i in xrange(self.current_frame_index + 1, len(self.movie), 1): bounds = self.movie.Frame(i).get_label(channel=self.channel, target=self.target) if bounds.is_keyframe(): next_keyframe = i break if next_keyframe >= 0: print next_keyframe self.interpolate(self.current_frame_index, next_keyframe) # find next keyframe prev_keyframe = -1 for i in xrange(self.current_frame_index - 1, -1, -1): bounds = self.movie.Frame(i).get_label(channel=self.channel, target=self.target) if bounds.is_keyframe(): prev_keyframe = i break if prev_keyframe >= 0: print prev_keyframe self.interpolate(prev_keyframe, self.current_frame_index) def run(self): while True: key = cv2.waitKey(0) cv2.imshow(self.win_name, self.display_image) if key == 65361: # left arrow # Go to previous frame self.advance_current_frame(-1) elif key == 65362: self.advance_current_frame(-10) elif key == 65363: # right arrow # Go to next frame self.advance_current_frame() elif key == 65364: self.advance_current_frame(10) elif key == ord('w'): # Write self.export_movie() if self.timer_lock.acquire(): if not self.refresh_timer.finished: self.refresh_timer.cancel() self.timer_lock.release() self.timer.cancel() self.reset() elif key == ord('t'): # Tracker self.toggle_tracker() elif key == ord(' '): # Advance tracker self.advance_tracker() self.needs_refresh = True elif key == ord('a'): self.x_size = int(self.x_size * 1.1) self.y_size = int(self.y_size * 1.1) self.needs_refresh = True elif key == ord('z'): self.x_size = int(self.x_size * 1 / 1.1) self.y_size = int(self.y_size * 1 / 1.1) self.needs_refresh = True elif key == ord('s'): self.y_size = int(self.y_size * 1.1) self.needs_refresh = True elif key == ord('k'): self.make_keyframe() elif key == ord('x'): self.y_size = int(self.y_size * 1 / 1.1) self.needs_refresh = True elif key == ord('q'): # Export while key != -1: for k in range(10): key = cv2.waitKey(10) print "Quiting! Do you want to save your work? [Y/N]" while True: key = cv2.waitKey(0) if key == ord('Y') or key == ord('y'): self.export_movie() break if key == ord('N') or key == ord('n'): break break logging.warning('Exiting') self.refresh_timer.cancel()
class LabelingApp(object): def __init__(self, filename, output=None, channel="default", target="default"): self.input_filename = filename if output is None: self.output_file = self.input_filename else: self.output_file = output self.channel = channel self.target = target self.reset() def reset(self, reload=False): cv2.destroyAllWindows() fc = FrameCollection() if reload: fc.load_from_file(filename=self.output_file) else: fc.load_from_file(filename=self.input_filename) self.movie = fc self.tracker = None self.current_frame_index = 0 self.right_button_pressed = False self.left_button_pressed = False self._anchor = None self.x_size = 30 self.y_size = 30 self._tracked_bounds = None self._stored_bounds = None self._current_bounds = BoundingRegion() # Make sure self.timer is set self.schedule_callback() # Initialization of the Graphic User Interface self.ready_to_refresh = threading.Lock() self.timer_lock = threading.Lock() self.win_name = 'Labeling video GUI' self.image = self.movie.Frame(self.current_frame_index).get_image(channel=self.channel) self.display_image = self.movie.Frame(self.current_frame_index).get_image(channel=self.channel) self.image_shape = self.image.shape self.create_windows() self.set_target_absent() self.image_buffer = {} self._last_update = time.time() self.refresh_timer = threading.Timer(0.05, self.refresh) self.refresh_timer.start() self.needs_refresh = True self.trim_end = len(self.movie)-1 self.trim_start = 0 def fill_up_the_buffer(self): r0 = max(self.current_frame_index-5, 0) r1 = min(self.current_frame_index+5, len(self.movie)) for i in xrange(r0, r1, 1): if i not in self.image_buffer.keys(): self.image_buffer[i] = self.movie.Frame(i).get_image(channel=self.channel) for i in self.image_buffer.keys(): if i < r0 or i > r1: del self.image_buffer[i] def create_windows(self): ''' Create playback progress bar (allow user to move along the movie) ''' cv2.namedWindow(self.win_name) cv2.moveWindow(self.win_name, 150, 150) cv2.namedWindow("completeness") cv2.moveWindow("completeness", 150, 50) cv2.setMouseCallback(self.win_name, self.trackbar_window_onMouse) cv2.createTrackbar("Frame", self.win_name, 0, len(self.movie) - 1, self.jump_to_frame_callback) cv2.createTrackbar("Trim start", self.win_name, 0, len(self.movie) - 1, self.set_trim_start) cv2.createTrackbar("Trim end", self.win_name, len(self.movie) - 1, len(self.movie) - 1, self.set_trim_end) im_height = 30 # 30 pixels high completeness bar im_width = len(self.movie) self.completeness_image = np.zeros((im_height, im_width, 3)) current_frame_index = int(im_width * float(self.current_frame_index) / len(self.movie)) self.completeness_image[:, current_frame_index, :] = 1. self.completeness = np.zeros(im_width, dtype=np.uint8) for i in xrange(len(self.movie)): bounds = self.movie.Frame(i).get_label(channel=self.channel, target=self.target) if bounds is None or bounds.empty: if bounds is None: self.movie.Frame(i).set_label(channel=self.channel, target=self.target, label=BoundingRegion()) self.completeness[i] = 2 else: if bounds.is_keyframe(): self.completeness[i] = 1 else: self.completeness[i] = 0 def set_trim_start(self, frame_index): self.trim_start = frame_index + 0 def set_trim_end(self, frame_index): self.trim_end = frame_index + 0 def jump_to_frame_callback(self, frame_index): self.current_frame_index = frame_index self.image = self.movie.Frame(self.current_frame_index).get_image(channel=self.channel) self.needs_refresh = True def update_completeness_window(self): if self.ready_to_refresh.acquire(False): self.completeness_image *= 0 self.completeness_image[:, self.current_frame_index, :] = 255 for i in xrange(len(self.movie)): self.completeness_image[:, i, self.completeness[i]] = 255 cv2.putText(self.completeness_image, '%i' % self.current_frame_index, (10, 10), cv2.FONT_HERSHEY_PLAIN, 1.0, (255, 255, 255), lineType=cv2.CV_AA) cv2.imshow('completeness', self.completeness_image) self.ready_to_refresh.release() def update_trackbar_window(self): if self.ready_to_refresh.acquire(): image = self.image.copy() self._stored_bounds = self.movie.Frame(self.current_frame_index).get_label(channel=self.channel, target=self.target) # display rectangle over selected area self._current_bounds.draw_box(image, color=(255, 255, 255)) self._current_bounds.draw_box(image, color=(0, 0, 0), thickness=1) # display rectangle over stored area if self._stored_bounds is not None: self._stored_bounds.draw_box(image, color=(255, 0, 0)) if self._tracked_bounds is not None: self._tracked_bounds.draw_box(image, color=(0, 255, 0)) # reshape to display if too small min_width = 320 if image.shape[1] < min_width: resize_height = int(0.5 + image.shape[0] * min_width / float(image.shape[1])) image = cv2.resize(image, (min_width, resize_height), interpolation=cv2.INTER_NEAREST) self.display_image=image self.ready_to_refresh.release() def refresh(self): if self.timer_lock.acquire(): if self.needs_refresh: self.update_trackbar_window() self.update_completeness_window() self.needs_refresh = False self.refresh_timer = threading.Timer(0.05, self.refresh) self.refresh_timer.start() self.timer_lock.release() def trackbar_window_onMouse(self, event, x, y, flags, _): cv2.imshow(self.win_name, self.display_image) # Update state self.right_button_pressed = (flags & cv2.EVENT_FLAG_RBUTTON) != 0 and event != cv2.EVENT_RBUTTONUP self.left_button_pressed = (flags & cv2.EVENT_FLAG_LBUTTON) != 0 and event != cv2.EVENT_LBUTTONUP # Set bounding box if event == cv2.EVENT_MBUTTONDOWN: if self._anchor is None: # Setting the anchor self._anchor = (x, y) else: # Defining the box self.save_and_advance() elif self._anchor is not None and event != cv2.EVENT_MBUTTONUP: # We are creating new bounding box from anchor to current position self.x_size = max(5, int(abs(x - self._anchor[0])+0.5)) self.y_size = max(5, int(abs(y - self._anchor[1])+0.5)) center_x = (x + self._anchor[0]) / 2 center_y = (y + self._anchor[1]) / 2 self.set_bounding_box_with_center(center_x, center_y) elif event == cv2.EVENT_MBUTTONUP: self.x_size = max(5, int(abs(x - self._anchor[0])+0.5)) self.y_size = max(5, int(abs(y - self._anchor[1])+0.5)) center_x = (x + self._anchor[0]) / 2 center_y = (y + self._anchor[1]) / 2 self.set_bounding_box_with_center(center_x, center_y) keyframe = False if flags & cv2.EVENT_FLAG_CTRLKEY != 0: keyframe = True self.save_and_advance(keyframe=keyframe) else: self.set_bounding_box_with_center(x, y) # If this is a left/right button down event, # we can advance one frame immediately # and then start the timer if event == cv2.EVENT_RBUTTONDOWN: self.set_target_absent() self.save_and_advance() self.right_button_pressed=True if self.timer.finished: # Rearm the timer self.schedule_callback() elif event == cv2.EVENT_LBUTTONDOWN: self.set_bounding_box_with_center(x, y) keyframe = False if flags & cv2.EVENT_FLAG_CTRLKEY != 0: keyframe = True self.save_and_advance(keyframe=keyframe) self.left_button_pressed=True if self.timer.finished: # Rearm the timer self.schedule_callback() elif not (self.right_button_pressed or self.left_button_pressed): # No mouse button pressed, so cancel the timer if it's running self.timer.cancel() if time.time()-self._last_update > 0.1: self._last_update = time.time() self.needs_refresh = True def set_target_absent(self): self._current_bounds = BoundingRegion() def set_bounding_box_with_center(self, x, y): box = [np.clip(int(x+0.5)-self.x_size/2, 0, self.image_shape[1]), np.clip(int(y+0.5)-self.y_size/2, 0, self.image_shape[0]), self.x_size + min(int(x+0.5)-self.x_size/2, 0), self.y_size + min(int(y+0.5)-self.y_size/2, 0)] self._current_bounds = BoundingRegion(box=box) self.needs_refresh = True def schedule_callback(self): # 0.2 secs => 5 Hz self.timer = threading.Timer(0.1, self.timer_callback) self.timer.start() def timer_callback(self): if self.right_button_pressed or self.left_button_pressed: self.save_and_advance() self.schedule_callback() def advance_current_frame(self, increment=1): self.current_frame_index = min(len(self.movie)-1, max(0, self.current_frame_index + increment)) self.image = self.movie.Frame(self.current_frame_index).get_image(channel=self.channel) cv2.setTrackbarPos("Frame", self.win_name, self.current_frame_index) if self.tracker is not None: self._tracked_bounds = self.tracker.track(self.movie.Frame(self.current_frame_index).get_image(channel=self.channel)) def save_and_advance(self, keyframe=False): self._anchor = None self.movie.Frame(self.current_frame_index).set_label(self._current_bounds.copy(), channel=self.channel, target=self.target) if self._current_bounds.empty: self.completeness[self.current_frame_index] = 2 else: if self._current_bounds.is_keyframe(): self.completeness[self.current_frame_index] = 1 else: self.completeness[self.current_frame_index] = 0 if keyframe: self.make_keyframe() self.advance_current_frame(15) else: self.advance_current_frame(1) def toggle_tracker(self): if self.tracker is None: self.tracker = CMTVisionTracker() self.tracker.prime(self.movie.Frame(self.current_frame_index).get_image(channel=self.channel), self._current_bounds) logging.warning("Tracker is enabled") else: self.tracker = None self._tracked_bounds = None logging.warning("Tracker is disabled") def advance_tracker(self): if self._tracked_bounds is not None: self._current_bounds=self._tracked_bounds self.save_and_advance() def export_movie(self): trim_frames_front = self.trim_start trim_frames_end = (len(self.movie) -1) - self.trim_end if trim_frames_end > 0: for i in range(trim_frames_end): self.movie.delete(-1) if trim_frames_front>0: for i in range(trim_frames_front): self.movie.delete(0) logging.warning("Exporting result in file " + self.output_file) self.movie.write_to_file(self.output_file) logging.warning("Exporting completed !") def interpolate(self, start, end): print "Interpolating %d %d" % (start, end) label0 = self.movie.Frame(start).get_label(channel=self.channel, target=self.target) label1 = self.movie.Frame(end).get_label(channel=self.channel, target=self.target) if (label0.empty) or (label1.empty): return box0 = label0.get_box_pixels() box1 = label1.get_box_pixels() for i in xrange(start+1, end, 1): alpha = (end-i)*1.0/(end-start) box2 = alpha*np.array(box0)+(1-alpha)*np.array(box1) box2 = map(lambda x: int(x), box2) self.movie.Frame(i).set_label(BoundingRegion(box=box2, image_shape=self.movie.Frame(i).get_image(channel=self.channel).shape), channel=self.channel, target=self.target) self.completeness[i] = 0 def make_keyframe(self): self.completeness[self.current_frame_index] = 1 self.movie.Frame(self.current_frame_index).get_label(channel=self.channel, target=self.target).set_keyframe(True) # find previous keyframe next_keyframe = -1 for i in xrange(self.current_frame_index+1, len(self.movie), 1): bounds = self.movie.Frame(i).get_label(channel=self.channel, target=self.target) if bounds.is_keyframe(): next_keyframe = i break if next_keyframe >= 0: print next_keyframe self.interpolate(self.current_frame_index, next_keyframe) # find next keyframe prev_keyframe = -1 for i in xrange(self.current_frame_index-1, -1, -1): bounds = self.movie.Frame(i).get_label(channel=self.channel, target=self.target) if bounds.is_keyframe(): prev_keyframe = i break if prev_keyframe >= 0: print prev_keyframe self.interpolate(prev_keyframe, self.current_frame_index) def run(self): while True: key = cv2.waitKey(0) cv2.imshow(self.win_name, self.display_image) if key == 65361: # left arrow # Go to previous frame self.advance_current_frame(-1) elif key == 65362: self.advance_current_frame(-10) elif key == 65363: # right arrow # Go to next frame self.advance_current_frame() elif key == 65364: self.advance_current_frame(10) elif key == ord('w'): # Write self.export_movie() if self.timer_lock.acquire(): if not self.refresh_timer.finished: self.refresh_timer.cancel() self.timer_lock.release() self.timer.cancel() self.reset() elif key == ord('t'): # Tracker self.toggle_tracker() elif key == ord(' '): # Advance tracker self.advance_tracker() self.needs_refresh = True elif key == ord('a'): self.x_size = int(self.x_size*1.1) self.y_size = int(self.y_size*1.1) self.needs_refresh = True elif key == ord('z'): self.x_size = int(self.x_size*1/1.1) self.y_size = int(self.y_size*1/1.1) self.needs_refresh = True elif key == ord('s'): self.y_size = int(self.y_size*1.1) self.needs_refresh = True elif key == ord('k'): self.make_keyframe() elif key == ord('x'): self.y_size = int(self.y_size*1/1.1) self.needs_refresh = True elif key == ord('q'): # Export while key != -1: for k in range(10): key = cv2.waitKey(10) print "Quiting! Do you want to save your work? [Y/N]" while True: key = cv2.waitKey(0) if key == ord('Y') or key == ord('y'): self.export_movie() break if key == ord('N') or key == ord('n'): break break logging.warning('Exiting') self.refresh_timer.cancel()
class PVMVisionTracker(GenericVisionTracker): """ This class exposes the null vision tracker which just always returns its priming bounding box """ def __init__(self, filename="", remote_filename="", cores="4", storage=None, steps_per_frame=1): """ Initialize the tracker """ self.name = 'PVMtracker' if filename == "": filename = storage.get(remote_filename) self.prop_dict = CoreUtils.load_model(filename) logging.info("Loaded the dictionary %s", filename) PVM_Create.upgrade_dictionary_to_ver1_0(self.prop_dict) self.prop_dict['num_proc'] = int(cores) for k in range(len(self.prop_dict['learning_rates'])): self.prop_dict['learning_rates'][k][0] = 0.0 logging.info("Setting learning rate in layer %d to zero" % k) self.prop_dict["readout_learning_rate"][0] = 0.0 logging.info("Setting readout learning rate to zero") self.manager = Manager(self.prop_dict, 1000) self.executor = CoreUtils.ModelExecution(prop_dict=self.prop_dict, manager=self.manager, port=9100) self.executor.start(blocking=False) self.threshold = 32 self.image_size = self.prop_dict['input_array'].shape[:2][::-1] self.readout_heatmap = np.zeros(self.image_size, dtype=np.float) self.step_per_frame = steps_per_frame def reset(self): """ Reset the tracker :return: """ self._primed = False self.bbox = None def _prime(self, im, bounding_region): """ prime tracker on image and bounding box :param im: input image (3 - channel numpy array) :type im: numpy.ndarray :param bounding_region: initial bounding region of the tracked object :type bounding_region: PVM_tools.bounding_region.BoundingRegion """ if not self._primed: logging.info("Priming the PVM tracker") logging.info("Setting up the tracker threshold to %d" % self.threshold) self._primed = True def _track(self, im): """ Track on given image, rseturn a bounding box :param im: image (3 - channel numpy array) :type im: numpy.ndarray :return: bounding box of the tracker object :rtype: PVM_tools.bounding_region.BoundingRegion """ # this is required so that tracker is re-initialized if it is primed again current_frame = cv2.resize(im, dsize=self.image_size) self.prop_dict['input_array'][:] = current_frame self.prop_dict['input_array_float'][:] = current_frame.astype(np.float)/255 for i in range(self.step_per_frame): self.executor.step() norm = 1.0 / len(self.prop_dict['predicted_readout_arrays']) self.readout_heatmap[:] = 0 for k in self.prop_dict['predicted_readout_arrays']: self.readout_heatmap[:] += cv2.resize(k.view(np.ndarray), dsize=self.image_size) * norm am = np.unravel_index(np.argmax(self.readout_heatmap), self.readout_heatmap.shape) if len(am) == 3: for d in range(3): if am[2] != d: self.readout_heatmap[:, :, d] = 0 self.heatmap = self.readout_heatmap.view(np.ndarray) if len(self.heatmap.shape) == 3: self.heatmap = np.max(self.heatmap, axis=2) self.heatmap = cv2.resize(self.heatmap, dsize=(im.shape[1], im.shape[0]), interpolation=cv2.INTER_CUBIC) self.heatmap = (self.heatmap * 255).astype(np.uint8) if np.max(self.heatmap) > (np.median(self.heatmap)+self.threshold): threshold=(np.max(self.heatmap)-np.median(self.heatmap))*0.5+np.median(self.heatmap) ret, thresh = cv2.threshold(self.heatmap, threshold, 255, 0) contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE) if len(contours) > 0: max_cnt=None for cnt in contours: pt = np.unravel_index(np.argmax(self.heatmap), self.heatmap.shape) if cv2.pointPolygonTest(cnt, (pt[1], pt[0]), False)>=0: max_cnt = cnt break if max_cnt is None: self.bbox = BoundingRegion() else: x, y, w, h = cv2.boundingRect(max_cnt) if w*h > 25: self.bbox = BoundingRegion(image_shape=im.shape, box=[x, y, w, h]) self.bbox.scale(1.1) else: self.bbox = BoundingRegion() else: self.bbox = BoundingRegion() else: self.bbox = BoundingRegion() self._primed = False return self.bbox.copy() def get_heatmap(self, heatmap_name=None): return self.heatmap() def finish(self): self.manager._running = False self.executor.step() self.executor.finish()