def loadFile(self, path): try: try: with open(path, "r") as data_file: data = json.load(data_file) self.loadData(data, path) except UnicodeDecodeError: with open(path, "rb") as data_file: byte_data = data_file.read() data = msgpack.unpackb(byte_data) self.loadData(data, path) except FileNotFoundError: LogObject().print("File {} not found".format(path)) return False except json.JSONDecodeError: LogObject().print("Invalid JSON file".format(path)) return False #except: # LogObject().print("Unexpected error:", sys.exc_info()[1]) # return False self.previous_path = path self.fast_save_enabled = True self.file_loaded_event.emit() return True
def saveDetectionsToFile(self, path): """ Writes current detections to a file at path. Values are separated by ';'. """ # Default formatting f1 = "{:.5f}" lineBase1 = "{};" + "{};{};{};".format(f1, f1, f1) try: with open(path, "w") as file: file.write( "frame;length;distance;angle;corner1 x;corner1 y;corner2 x;corner2 y;corner3 x;corner3 y;corner4 x;corner4 y\n" ) for frame, dets in enumerate(self.detections): if dets is not None: for d in dets: if d.corners is not None: file.write( lineBase1.format(frame, d.length, d.distance, d.angle)) file.write(d.cornersToString(";")) file.write("\n") LogObject().print("Detections saved to path:", path) except PermissionError as e: LogObject().print( "Cannot open file {}. Permission denied.".format(path))
def __init__(self, pol_shape, cart_height, radius_limits, beam_angle): """ Initializes the mapping function. Parameters: pol_shape -- Shape of the polar frame cart_height -- Height of the cartesian (output) image. radius_limits -- Min and max radius of the beam. beam_angle -- Angle covered by the beam (radians). """ LogObject().print("Init mapping") self.pol_shape = pol_shape self.setCartShape(cart_height, beam_angle) self.radius_limits = radius_limits self.angle_limits = (np.pi / 2 - beam_angle / 2, np.pi / 2 + beam_angle / 2) self.center = (0, (self.cart_shape[1] - 1) / 2) self.metric_cart_shape = (radius_limits[1], self.cart_shape[1] / self.cart_shape[0] * radius_limits[1]) self.map_x = np.zeros(self.cart_shape, dtype=np.float32) self.map_y = np.zeros(self.cart_shape, dtype=np.float32) for j in range(self.cart_shape[0]): if j % 100 == 0: LogObject().print("Mapping:", j) for i in range(self.cart_shape[1]): _j = self.cart_shape[0] - j - 1 self.map_y[j, i], self.map_x[j, i] = self.cart2polImage(_j, i) LogObject().print("End mapping")
def track(self): """ Handles the tracking process. Opens file and connects detection and tracking calls to the appropriate signals, so that they can be started when the file has been loaded. """ if self.testFile: self.playback_manager.openTestFile() else: self.playback_manager.loadFile(self.file) LogObject().print("Frame count:", self.playback_manager.getFrameCount()) if self.display: self.playback_manager.frame_available.connect( self.forwardImageDisplay) else: self.playback_manager.frame_available.connect(self.forwardImage) self.detector.mog_parameters.nof_bg_frames = 500 self.detector._show_detections = True self.playback_manager.mapping_done.connect(self.startTrackingProcess) self.tracker.all_computed_signal.connect(self.onAllComputed) if self.display: self.figure = TestFigure(self.playback_manager.togglePlay) self.main_window.setCentralWidget(self.figure) LogObject().print(self.detector.parameters) LogObject().print(self.detector.parameters.mog_parameters) LogObject().print(self.tracker.parameters) if self.display: self.main_window.show()
def loadDetectionsFromFile(self, path): """ Loads a file from path. Values are expected to be separated by ';'. """ try: with open(path, 'r') as file: self.clearDetections() nof_frames = self.image_provider.getFrameCount() ignored_dets = 0 header = file.readline() for line in file: split_line = line.split(';') frame = int(split_line[0]) if frame >= nof_frames: ignored_dets += 1 continue length = float(split_line[1]) distance = float(split_line[2]) angle = float(split_line[3]) c1 = [float(split_line[5]), float(split_line[4])] c2 = [float(split_line[7]), float(split_line[6])] c3 = [float(split_line[9]), float(split_line[8])] c4 = [float(split_line[11]), float(split_line[10])] corners = np.array([c1, c2, c3, c4]) det = Detection(0) det.init_from_file(corners, length, distance, angle) if self.detections[frame] is None: self.detections[frame] = [det] else: self.detections[frame].append(det) self.updateVerticalDetections() self.compute_on_event = False if ignored_dets > 0: LogObject().print( "Encountered {} detections that were out of range {}.". format(ignored_dets, nof_frames)) except PermissionError as e: LogObject().print( "Cannot open file {}. Permission denied.".format(path))
def data(self, index, role): if role == Qt.DisplayRole: row = index.row() col = index.column() if row >= len(self.fish_list): LogObject().print("Bad index {}/{}".format(row, len(self.fish_list) - 1)) return QtCore.QVariant() if col == 0: return self.fish_list[row].id elif col == 1: return self.fish_list[row].length elif col == 2: return self.fish_list[row].direction.name elif col == 3: return self.fish_list[row].frame_in elif col == 4: return self.fish_list[row].frame_out elif col == 5: return self.fish_list[row].duration elif col == 6: return len(self.fish_list[row].tracks) else: return QtCore.QVariant() else: return QtCore.QVariant()
def __init__(self, display, files, save_directory, parallel=1, create_directory=True): super().__init__() LogObject().print("Display: ", display) self.files = files self.display = display if create_directory: date_time_directory = "batch_{}".format( datetime.now().strftime("%Y-%m-%d-%H%M%S")) self.save_directory = os.path.join(save_directory, date_time_directory) if not os.path.exists(self.save_directory): os.mkdir(self.save_directory) else: self.save_directory = save_directory self.thread_pool = QtCore.QThreadPool() self.thread_pool.setMaxThreadCount(parallel + 1) self.processes = [] self.active_processes = [] self.state = ProcessState.INITIALIZING self.exit_time = time.time() self.n_processes = 0 self.total_processes = len(self.files)
def finishTerminated(self): """ Handles the shutdown process initiated by method terminate. """ for proc_info in self.processes: try: proc_info.connection.send((-1, "Terminate")) except BrokenPipeError as e: # Process not yet active pass while True: try: id, msg = proc_info.connection.recv() if id == -1: break except EOFError: break except ValueError: # Received message with no id continue LogObject().print("File [{}] terminated.".format(proc_info.id)) for proc_info in self.processes: if proc_info.process is not None: proc_info.process.terminate() self.exit_signal.emit(False)
def updateVerticalDetections(self): LogObject().print("Updated") self.vertical_detections = [ [d.center[0] for d in dets if d.center is not None] if dets is not None else [] for dets in self.detections ]
def applySaveDictionary(self, data, dets): """ Load fish entries from data provided by SaveManager. """ self.clear() for id, f_data in data.items(): f = None for frame, det_label, track in f_data: if f is None: f = FishEntry(id, frame, frame) if det_label is not None: # Goes through detections in the same frame and tries to assign the # corresponding detection based on the label. frame_dets = dets[frame] match_found = False for fd in frame_dets: if fd.label == det_label: # Adds track with a matching detection to the FishEntry f.addTrack(track, fd, frame) match_found = True break if not match_found: LogObject().print("Warning: Match not found in frame {} for label {}".format(frame, det_label)) else: f.addTrack(track, None, frame) self.all_fish[id] = f self.trimFishList()
def terminate(self): """ Clears the thread pool and sets system state to TERMINATING, which leads to clean shutdown of the processes. """ self.thread_pool.clear() LogObject().print("Terminating") self.state = ProcessState.TERMINATING
def run(self): self.showWindow() self.detector.initMOG() for i in range(self.getFrameCount()): self.readParameters() images = self.detector.compute(i, self.getFrame(i), True) LogObject().print(images) self.updateWindows(*images)
def clearDetections(self): LogObject().print("Cleared") nof_frames = self.image_provider.getFrameCount() self.detections = [None] * nof_frames self.vertical_detections = [] #self.detections_clearable = False self.applied_parameters = None self.compute_on_event = True self.state_changed_event()
def setParameterDict(self, dict): for key, value in dict.items(): if hasattr(self, key) and key in PARAMETER_TYPES: try: setattr(self, key, PARAMETER_TYPES[key](value)) except ValueError as e: LogObject().print( "Error: Invalid value in detector parameters file,", e) elif hasattr(self.mog_parameters, key) and key in PARAMETER_TYPES: try: setattr(self.mog_parameters, key, PARAMETER_TYPES[key](value)) except ValueError as e: LogObject().print( "Error: Invalid value in detector parameters file,", e) else: LogObject().print("Error: Invalid parameters: {}: {}".format( key, value))
def trackAll(self, detection_frames): """ Tracks all detections in the given frames. Updates tracks_by_frame and signals when the computation has finished. """ self.tracking = True self.stop_tracking = False self.state_changed_signal.emit() self.init_signal.emit() if self.detector.allCalculationAvailable(): self.detector.computeAll() if self.detector.allCalculationAvailable(): LogObject().print("Stopped before tracking.") self.abortComputing(True) return count = len(detection_frames) self.tracks_by_frame = {} self.mot_tracker = Sort(max_age=self.parameters.max_age, min_hits=self.parameters.min_hits, search_radius=self.parameters.search_radius) KalmanBoxTracker.count = 0 ten_perc = 0.1 * count print_limit = 0 for i, dets in enumerate(detection_frames): if i > print_limit: LogObject().print("Tracking:", int(float(i) / count * 100), "%") print_limit += ten_perc if self.stop_tracking: LogObject().print("Stopped tracking at", i) self.abortComputing(False) return self.tracks_by_frame[i] = self.trackBase(dets, i) LogObject().print("Tracking: 100 %") self.tracking = False self.applied_parameters = self.parameters.copy() self.applied_detector_parameters = self.detector.parameters.copy() self.state_changed_signal.emit() self.all_computed_signal.emit()
def connectToLogObject(self, format=None): """ Connect text_edit field to LogObject signal. A formatting function, which takes a string as input and returns the modified string, can be provided for custom formatting, e.g. for adding a time stamp. """ log = LogObject() if format: log.connect(lambda s: self.appendText(format(s))) else: log.connect(lambda s: self.appendText(s + "\n")) log.disconnectDefault()
def update_use_times(author): """ 「./user_log/ユーザー名.csv」を読み込み、「date」、「use_times」列で一意のkeyとして 利用回数をインクリメントして更新する Parameters ------------------------------ author : str ユーザー名 Returns ------------------------------ Nothing """ current_date = datetime.date.today().strftime( '%Y-%m-%d') #date型のままだとqueryの条件で型が一致しないため、strに変換する user_log_data = pd.read_csv(f'{USER_FILE_PATH}/{author}.csv', encoding='utf-8', engine='python') current_user_data = user_log_data.query( 'user_id == @author and date == @current_date') log_obj = LogObject() log_obj.user_id = author log_obj.date = current_date if len(current_user_data) == 0: # 今日初めての利用の場合は、行が存在しないので、利用回数1回の行を新規で作成し、DataFrameに追加してcsvを更新する log_obj.use_times = 1 s = pd.Series([log_obj.user_id, log_obj.date, log_obj.use_times], index=user_log_data.columns) user_log_data = user_log_data.append(s, ignore_index=True) elif current_user_data['use_times'].values[0] != DATE_USE_LIMIT: # 今日の今までの利用回数が19回以下の場合は、該当ユーザーのDataFrameの利用回数を+1してcsvを更新する use_times = current_user_data['use_times'].values[0] + 1 user_log_data.loc[(user_log_data['user_id'] == author) & (user_log_data['date'] == current_date), ['use_times']] = use_times user_log_data.to_csv(f'{USER_FILE_PATH}/{author}.csv', encoding='utf-8', index=False)
def saveToFile(self, path): """ Tries to save all fish information (from all_fish dictionary) to a file. """ if(self.playback_manager.playback_thread is None): LogObject().print("No file open, cannot save.") return try: with open(path, "w") as file: file.write("id;frame;length;distance;angle;direction;corner1 x;corner1 y;corner2 x;corner2 y;corner3 x;corner3 y;corner4 x;corner4 y; detection\n") lines = self.getSaveLines() lines.sort(key = lambda l: (l[0].id, l[1])) for _, _, line in lines: file.write(line) LogObject().print("Tracks saved to path:", path) except PermissionError as e: LogObject().print("Cannot open file {}. Permission denied.".format(path))
def init_from_file(self, corners, length, distance, angle): """ Initialize detection parameters from a csv file. Data is not stored when exporting a csv file, which means it cannot be recovered here. This mainly affects the visualization of the detection. """ self.corners = np.array(corners) self.center = np.average(self.corners, axis=0) LogObject().print(self.center) self.length = length self.distance = distance self.angle = angle
def onBatchExit(self, finished): """ Called when the system is ready to start a new batch. """ LogObject().print("--- On batch exit ---") self.start_btn.setText("Start") self.start_btn.setEnabled(True) self.batch_track = None self.setStatusLabel()
def beginTrack(self, test=False): """ For each file in files, creates a Worker that runs track and places it in thread_pool. Main thread is occupied with a call to communicate method. """ self.state = ProcessState.RUNNING id = 0 worker = Worker(self.communicate) self.thread_pool.start(worker) if test: # If using test file (defined in conf.json) parent_conn, child_conn = mp.Pipe() file = fh.getTestFilePath() proc_info = ProcessInfo(id, file, parent_conn) self.processes.append(proc_info) worker = Worker(self.track, proc_info, child_conn, True) self.thread_pool.start(worker) LogObject().print("Created Worker for file " + file) self.n_processes = 1 self.total_processes = 1 else: # Normal use for file in self.files: parent_conn, child_conn = mp.Pipe() proc_info = ProcessInfo(id, file, parent_conn) self.processes.append(proc_info) worker = Worker(self.track, proc_info, child_conn, False) self.thread_pool.start(worker) LogObject().print("Created Worker for file " + file) id += 1 self.n_processes += 1 LogObject().print("Total processes:", self.n_processes)
def playbackTest(): """ Test code to assure tracker works with detector. """ def forwardImage(tuple): ind, frame = tuple detections = detector.getDetection(ind) image = cv2.applyColorMap(frame, cv2.COLORMAP_OCEAN) image = tracker.visualize(image, ind) figure.displayImage((ind, image)) def startDetector(): detector.initMOG() detector.computeAll() tracker.trackAll(detector.detections) playback_manager.play() app = QtWidgets.QApplication(sys.argv) main_window = QtWidgets.QMainWindow() playback_manager = PlaybackManager(app, main_window) detector = Detector(playback_manager) tracker = Tracker(detector) playback_manager.fps = 10 playback_manager.openTestFile() playback_manager.frame_available.connect(forwardImage) detector.mog_parameters.nof_bg_frames = 500 detector._show_detections = True playback_manager.mapping_done.connect(startDetector) figure = TestFigure(playback_manager.togglePlay) main_window.setCentralWidget(figure) LogObject().print(detector.parameters) LogObject().print(detector.parameters.mog_parameters) LogObject().print(tracker.parameters) main_window.show() sys.exit(app.exec_())
def loadData(self, data, path): try: file_path = os.path.abspath(data["path"]) secondary_path = os.path.abspath( os.path.join(os.path.dirname(path), os.path.basename(file_path))) self.playback_manager.checkLoadedFile(file_path, secondary_path, True) self.fish_manager.setUpDownInversion(data["inverted upstream"]) self.detector.parameters.setParameterDict(data["detector"]) self.tracker.parameters.setParameterDict(data["tracker"]) self.detector.applySaveDictionary(data["detections"]) dets = self.detector.detections self.fish_manager.applySaveDictionary(data["fish"], dets) except ValueError as e: LogObject().print("Error: Invalid value(s) in save file,", e) self.playback_manager.closeFile() except KeyError as e: LogObject().print("Error: Invalid key(s) in save file,", e) self.playback_manager.closeFile()
def computeAll(self): self.computing = True self.stop_computing = False self.compute_on_event = False self.state_changed_event() if self.mogParametersDirty(): self.initMOG() if self.mogParametersDirty(): LogObject().print("Stopped before detecting.") self.abortComputing(True) return count = self.image_provider.getFrameCount() ten_perc = 0.1 * count print_limit = 0 for ind in range(count): if ind > print_limit: LogObject().print("Detecting:", int(float(ind) / count * 100), "%") print_limit += ten_perc if self.stop_computing: LogObject().print("Stopped detecting at", ind) self.abortComputing(False) return img = self.image_provider.getFrame(ind) self.computeBase(ind, img) LogObject().print("Detecting: 100 %") self.computing = False #self.detections_clearable = True self.applied_parameters = self.parameters.copy() self.updateVerticalDetections() self.state_changed_event() self.all_computed_event()
def processFinished(self, proc_info): """ Reduces n_processes by one and if none are remaining, emits the exit_signal """ LogObject().print("File {} finished.".format(proc_info.file)) self.n_processes -= 1 if self.n_processes <= 0: # Let main thread (running communicate) know the process is about to quit # and emit exit signal. if self.state is not ProcessState.TERMINATING: self.state = ProcessState.FINISHED self.exit_time = time.time() self.exit_signal.emit(True)
def removeFish(self, rows, update=True): if(len(rows) > 0): for row in sorted(rows, reverse=True): if row >= len(self.fish_list): continue fish_id = self.fish_list[row].id try: del_f = self.all_fish.pop(fish_id) del del_f except KeyError: LogObject().print("KeyError occured when removing entry with id:", fish_id) if update: self.trimFishList()
def communicate(self): """ Polls through all running processes and forwards all messages to LogObject. """ while self.state is ProcessState.RUNNING or self.state is ProcessState.INITIALIZING or time.time( ) < self.exit_time + 1: for proc_info in self.processes: if (proc_info.process and proc_info.process.is_alive() and proc_info.connection.poll()): LogObject().print(proc_info.id, proc_info.connection.recv(), end="") time.sleep(0.01) if self.state is ProcessState.TERMINATING: self.finishTerminated()
def __init__(self, app, display, file, save_directory, connection=None, testFile=False): super().__init__() self.app = app self.display = display self.figure = None self.file = file self.save_directory = os.path.abspath(save_directory) self.connection = connection self.testFile = testFile self.alive = True self.save_detections = True self.save_tracks = True if display: self.main_window = QtWidgets.QMainWindow() else: self.main_window = None self.playback_manager = PlaybackManager(self.app, self.main_window) self.detector = Detector(self.playback_manager) self.tracker = Tracker(self.detector) self.fish_manager = FishManager(self.playback_manager, self.tracker) self.playback_manager.fps = 100 self.playback_manager.runInThread(self.listenConnection) log = LogObject() log.disconnectDefault() #log.connect(writeToFile) log.connect(self.writeToConnection) log.print("Process created for file: ", self.file)
def showFish(self, fishNumber, inputDict): ## TODO _ # ffigure = self.MyFigureWidget # ffigure.clear() # self.MyFigureWidget.clear() counter = 0 LogObject().print("Fish = ", fishNumber) for i in inputDict["frames"]: # ffigure.setUpdatesEnabled(False) self.UI_FRAME_INDEX = i x = int( inputDict["locations"][counter][0]) y = int( inputDict["locations"][counter][1]) self.marker = str(x)+','+str(y) self.FSlider.setValue(self.UI_FRAME_INDEX) self.marker = None self.repaint() counter +=1 self.marker = None return
def trackBase(self, frame, ind): """ Performs tracking step for a single frame. Returns (track, detection) if the track was updated this frame, otherwise (track, None). """ if frame is None: LogObject().print( "Invalid detector results encountered at frame " + str(ind) + ". Consider rerunning the detector.") return self.mot_tracker.update() detections = [d for d in frame if d.corners is not None] if len(detections) > 0: dets = np.array([ np.min(d.corners, 0).flatten().tolist() + np.max(d.corners, 0).flatten().tolist() for d in detections ]) tracks = self.mot_tracker.update(dets) else: tracks = self.mot_tracker.update() return [(tr, detections[int(tr[7])]) if tr[7] >= 0 else (tr, None) for tr in tracks]