class Ui_MainWindow(QMainWindow, Ui_MainWindowBase): def __init__(self): super(Ui_MainWindow, self).__init__() self.setupUi(self) self.videoPlaybackInit() self.imgInit() self.menuInit() self.fgbg = None self.filePath = None self.df = None self.df_dist = None self.df_region = None self.relation_matrix = None self.trackingPathGroup = None self.currentFrameNo = None self.graphics_items = {} self.plot_widgets = [] factory = QItemEditorFactory() factory.registerEditor(QVariant.Color, ColorListItemEditorCreator()) self.createGUI() self.chord_diagram_dialog = ChordDiagramDialog(self) self.timeline_diagram_dialog = TimelineDiagramDialog(self) self.savedFlag = True # dialog = TimelineDiagramDialog(self) # dialog.show() def createGUI(self): colorEditorFactory = QItemEditorFactory() colorEditorFactory.registerEditor(QVariant.Color, ColorListItemEditorCreator()) colorEditorDelegate = QStyledItemDelegate(self) colorEditorDelegate.setItemEditorFactory(colorEditorFactory) figureEditorFactory = QItemEditorFactory() figureEditorFactory.registerEditor(QVariant.String, FigureListItemEditorCreator()) figureEditorDelegate = QStyledItemDelegate(self) figureEditorDelegate.setItemEditorFactory(figureEditorFactory) self.regionTableWidget.cellChanged.connect(self.regionTableWidgetCellChanged) self.regionTableWidget.setColumnCount(3) self.regionTableWidget.setItemDelegateForColumn(1, colorEditorDelegate) self.regionTableWidget.setItemDelegateForColumn(2, figureEditorDelegate) self.regionTableWidget.setHorizontalHeaderLabels(["Name", "Color", "Type"]) self.regionTableWidget.verticalHeader().setVisible(False) self.regionTableWidget.resize(150, 50) qApp = QtWidgets.qApp self.upRegionButton.setIcon(qApp.style().standardIcon(QStyle.SP_ArrowUp)) self.downRegionButton.setIcon(qApp.style().standardIcon(QStyle.SP_ArrowDown)) self.addRegionButton.clicked.connect(self.addRegionButtonClicked) self.removeRegionButton.clicked.connect(self.removeRegionButtonClicked) self.upRegionButton.clicked.connect(self.upRegionButtonClicked) self.downRegionButton.clicked.connect(self.downRegionButtonClicked) self.radiusSpinBox.valueChanged.connect(self.radiusSpinBoxValueChanged) def radiusSpinBoxValueChanged(self, i): if self.trackingPathGroup is not None: self.trackingPathGroup.setRadius(i) self.updateInputGraphicsView() def addRegionButtonClicked(self): if not self.videoPlaybackWidget.isOpened(): return name_num = self.regionTableWidget.rowCount() for i, name in enumerate(map(lambda x: x.data(Qt.DisplayRole), self.getCol(0))): try: val = int(name) except: continue name_num = max(name_num, val+1) self.addRow(str(name_num), QColor('red'), FigureType.Recutangular) def removeRegionButtonClicked(self): if not self.videoPlaybackWidget.isOpened(): return if not len(self.regionTableWidget.selectedItems()) > 0: return selected_row = self.regionTableWidget.row(self.regionTableWidget.selectedItems()[0]) name_item = self.regionTableWidget.item(selected_row, 0) name = name_item.data(Qt.UserRole) item = self.graphics_items.pop(name) if item is not None: self.inputScene.removeItem(item) self.regionTableWidget.removeRow(selected_row) def upRegionButtonClicked(self): if not self.videoPlaybackWidget.isOpened(): return self.moveRow(True) def downRegionButtonClicked(self): if not self.videoPlaybackWidget.isOpened(): return self.moveRow(False) def addRow(self, name, color, figType): i = self.regionTableWidget.rowCount() self.regionTableWidget.insertRow(i) nameItem = QTableWidgetItem(name) nameItem.setData(Qt.UserRole, name) nameItem.setFlags(Qt.ItemIsEditable | Qt.ItemIsSelectable | Qt.ItemIsEnabled) colorItem = QTableWidgetItem() colorItem.setData(Qt.BackgroundRole, color) colorItem.setData(Qt.DisplayRole, color) figItem = QTableWidgetItem() figItem.setData(Qt.DisplayRole, figType.name) self.regionTableWidget.setItem(i, 0, nameItem) self.regionTableWidget.setItem(i, 1, colorItem) self.regionTableWidget.setItem(i, 2, figItem) self.regionTableWidget.resizeColumnToContents(0) self.regionTableWidget.horizontalHeader().setStretchLastSection(True) def regionTableWidgetCellChanged(self, row, col): changed_item = self.regionTableWidget.item(row, col) if col==0: old_name = changed_item.data(Qt.UserRole) new_name = changed_item.data(Qt.DisplayRole) if old_name!=new_name: for i, name in enumerate(map(lambda x: x.data(Qt.DisplayRole), self.getCol(0))): if i==row: continue if name==new_name: changed_item.setData(Qt.DisplayRole, old_name) return self.graphics_items[new_name] = self.graphics_items.pop(old_name) item = self.graphics_items[new_name] if item is not None: item.setObjectName(new_name) changed_item.setData(Qt.UserRole, new_name) if col==1: try: bg_color = changed_item.data(Qt.BackgroundRole) disp_color = changed_item.data(Qt.DisplayRole) if disp_color.name() != bg_color.name(): bg_color.setNamedColor(disp_color.name()) changed_item.setData(Qt.BackgroundRole, bg_color) name_item = self.regionTableWidget.item(row, 0) name = name_item.data(Qt.DisplayRole) item = self.graphics_items[name] if item is not None: item.setColor(disp_color) except: pass if col==2: old_type = changed_item.data(Qt.UserRole) new_type = FigureType[changed_item.data(Qt.DisplayRole)] if new_type is not old_type: name_item = self.regionTableWidget.item(row, 0) name = name_item.data(Qt.UserRole) item = self.getGraphicsItemFromInputScene(name) if item is not None: self.inputScene.removeItem(item) new_fig = new_type.value() new_fig.setObjectName(name) self.graphics_items[name] = new_fig if new_type is not FigureType.Point: new_fig.setZValue(1000-10*row) else: new_fig.autoAdjustRadius(self.cv_img.shape) new_fig.setColor(self.regionTableWidget.item(row, 1).data(Qt.BackgroundRole)) self.inputScene.addItem(new_fig) changed_item.setData(Qt.UserRole, new_type) height, width, dim = self.cv_img.shape if new_type is FigureType.Point: array = np.array([0.5*width, 0.5*height]) elif new_type is FigureType.Polygon: array = [ [0.1*width, 0.1*height], [0.9*width, 0.1*height], [0.9*width, 0.9*height], [0.1*width, 0.9*height] ] else: array = [[0.1*width, 0.1*height], [0.9*width, 0.9*height]] new_fig.setPoints(array) self.updateInputGraphicsView() def moveRow(self, up): if not len(self.regionTableWidget.selectedItems()) > 0: return source_row = self.regionTableWidget.row(self.regionTableWidget.selectedItems()[0]) if up: dest_row = source_row-1 else: dest_row = source_row+1 if not (dest_row >= 0 and dest_row < self.regionTableWidget.rowCount()): return source_type = self.regionTableWidget.item(source_row, 2).data(Qt.UserRole) dest_type = self.regionTableWidget.item(dest_row, 2).data(Qt.UserRole) if source_type is not FigureType.Point and dest_type is not FigureType.Point: source_name = self.regionTableWidget.item(source_row, 0).data(Qt.DisplayRole) source_fig_item = self.graphics_items[source_name] source_z = source_fig_item.zValue() dest_name = self.regionTableWidget.item(dest_row, 0).data(Qt.DisplayRole) dest_fig_item = self.graphics_items[dest_name] dest_z = dest_fig_item.zValue() source_fig_item.setZValue(dest_z) dest_fig_item.setZValue(source_z) # take whole rows sourceItems = self.takeRow(source_row) destItems = self.takeRow(dest_row) # set back in reverse order self.setRow(source_row, destItems) self.setRow(dest_row, sourceItems) def takeRow(self, row): rowItems = [] for col in range(self.regionTableWidget.columnCount()): rowItems.append(self.regionTableWidget.takeItem(row, col)) return rowItems def getCol(self, col): colItems = [] for row in range(self.regionTableWidget.rowCount()): colItems.append(self.regionTableWidget.item(row, col)) return colItems def setRow(self, row, rowItems): for col in range(self.regionTableWidget.columnCount()): self.regionTableWidget.setItem(row, col, rowItems[col]) def getGraphicsItemFromInputScene(self, name): for item in self.inputScene.items(): #QGraphicsObjectをSceneから取り出そうとすると, #親クラスであるQGraphicsItem(QPixmapGraphicsItem)にダウンキャスト #されて返ってくるためtryが必要. try: if name == item.objectName(): return item except: pass return None def dragEnterEvent(self,event): event.acceptProposedAction() def dropEvent(self,event): mime = event.mimeData() if mime.hasUrls(): urls = mime.urls() if len(urls) > 0: self.processDropedFile(urls[0].toLocalFile()) event.acceptProposedAction() def closeEvent(self, event): if self.df is None or self.savedFlag: return quit_msg = "Data is not saved.\nAre you sure you want to exit the program?" reply = QtWidgets.QMessageBox.question( self, 'Warning', quit_msg, QtWidgets.QMessageBox.Yes, QtWidgets.QMessageBox.No ) if reply == QtWidgets.QMessageBox.Yes: self.closeDialog() event.accept() else: event.ignore() def openTrackingPathColorSelectorDialog(self, activated=False): if self.trackingPathGroup is not None: self.trackingPathGroup.openColorSelectorDialog(self) def processDropedFile(self,filePath): root,ext = os.path.splitext(filePath) if ext == ".csv": self.openCSVFile(filePath=filePath) elif self.openVideoFile(filePath=filePath): return def videoPlaybackInit(self): self.videoPlaybackWidget.hide() self.videoPlaybackWidget.frameChanged.connect(self.setFrame, type=Qt.QueuedConnection) def setFrame(self, frame, frameNo): if frame is not None: self.cv_img = frame self.currentFrameNo = frameNo self.updateInputGraphicsView() self.evaluate() def imgInit(self): self.cv_img = cv2.imread(os.path.join(sampleDataPath,"color_filter_test.png")) self.inputScene = QGraphicsScene() self.inputGraphicsView.setScene(self.inputScene) self.inputGraphicsView.resizeEvent = self.inputGraphicsViewResized qimg = misc.cvMatToQImage(self.cv_img) self.inputPixMap = QPixmap.fromImage(qimg) self.inputPixMapItem = QGraphicsPixmapItem(self.inputPixMap) self.inputScene.addItem(self.inputPixMapItem) def menuInit(self): self.actionOpenVideo.triggered.connect(self.openVideoFile) self.actionOpenCSVFile.triggered.connect(self.openCSVFile) self.actionSaveCSVFile.triggered.connect(self.saveCSVFile) self.actionCalculate.triggered.connect(self.process) self.actionTrackingPathColor.triggered.connect(self.openTrackingPathColorSelectorDialog) def openVideoFile(self, activated=False, filePath = None): if filePath is None: filePath, _ = QFileDialog.getOpenFileName(None, 'Open Video File', userDir) if len(filePath) is not 0: self.filePath = filePath self.fgbg = None ret = self.videoPlaybackWidget.openVideo(filePath) if ret == False: return False self.videoPlaybackWidget.show() self.cv_img = self.videoPlaybackWidget.getCurrentFrame() self.initialize() return True def openCSVFile(self, activated=False, filePath=None): if filePath is None: filePath, _ = QFileDialog.getOpenFileName(None, 'Open CSV File', userDir, 'CSV files (*.csv)') if len(filePath) is not 0: self.filePath = filePath self.df = pd.read_csv(filePath, index_col=0) if self.trackingPathGroup is not None: self.inputScene.removeItem(self.trackingPathGroup) self.trackingPathGroup = TrackingPathGroup() self.trackingPathGroup.setRect(self.inputScene.sceneRect()) self.inputScene.addItem(self.trackingPathGroup) self.trackingPathGroup.setDataFrame(self.df) self.initialize() def updateInputGraphicsView(self): self.inputScene.removeItem(self.inputPixMapItem) qimg = misc.cvMatToQImage(self.cv_img) self.inputPixMap = QPixmap.fromImage(qimg) rect = QtCore.QRectF(self.inputPixMap.rect()) self.inputScene.setSceneRect(rect) self.inputPixMapItem = QGraphicsPixmapItem(self.inputPixMap) self.inputScene.addItem(self.inputPixMapItem) self.inputGraphicsView.viewport().update() self.inputGraphicsViewResized() def inputGraphicsViewResized(self, event=None): self.inputGraphicsView.fitInView(QtCore.QRectF(self.inputPixMap.rect()), QtCore.Qt.KeepAspectRatio) def closeDialog(self): self.chord_diagram_dialog.hide() self.timeline_diagram_dialog.hide() for plot_widget in self.plot_widgets: plot_widget.window().close() self.plot_widgets.clear() def process(self, activated=False): # TODO: 分割. # if self.df is None or len(self.getCol(0))==0: # return if self.df is None: return self.closeDialog() names = list(map(lambda x: x.data(Qt.UserRole), self.getCol(0))) items = [self.graphics_items[name] for name in names] colors = {('no'+name):color.data(Qt.BackgroundRole) for name, color in zip(names, self.getCol(1))} region_list = list(filter(lambda x:type(x[1]) is not FigureType.Point.value, zip(names, items))) point_list = list(filter(lambda x:type(x[1]) is FigureType.Point.value, zip(names, items))) col_n = int(len(self.df.columns)/2) columns = ["{0}_{1}".format(name, col) for col in range(col_n) for name, _ in point_list] df = self.df.copy() df.columns = list(range(2*col_n)) self.df_region = pd.DataFrame( index=df.index, columns=range(col_n) ) self.df_dist = pd.DataFrame( index=df.index, columns=columns ) progress = QProgressDialog("Running...", "Abort", 0, len(df.index), self) progress.setWindowModality(Qt.WindowModal) interactions = [0 for i in range(int(nCr(col_n, 2)))] radius = 2*self.trackingPathGroup.getRadius() # radius = 100 for i, row in enumerate(df.index): progress.setValue(i) if progress.wasCanceled(): break pts = [] for col in range(col_n): pt = np.array(df.loc[row, 2*col:2*col+1]) pts.append(pt) for name, item in point_list: self.df_dist.loc[row, "{0}_{1}".format(name, col)] = item.distance(pt) for name, item in region_list: # TODO: 領域がかぶったときの挙動がアヤシい.要チェック. if item.includes(pt): self.df_region.loc[row, col] = name break for count, (p1, p2) in enumerate(itertools.combinations(pts, 2)): d = np.linalg.norm(p1-p2) if d <= radius: interactions[count] += 1 progress.setValue(len(df.index)) matrix = np.zeros((col_n, col_n)) for pos, (i, j) in enumerate(itertools.combinations(range(col_n), 2)): matrix[i, j] = interactions[pos] matrix[j, i] = interactions[pos] for name, item in point_list: plot_widget = pg.plot(title="Point: "+name) plot_widget.addLegend() plot_item = plot_widget.getPlotItem() bottom_axis = plot_item.getAxis("bottom") bottom_axis.setLabel("# of Frame") left_axis = plot_item.getAxis("left") left_axis.setLabel("Distance [pixel]") for col, color in zip(range(col_n), self.trackingPathGroup.getColors()): pen = QPen(QColor(color)) pen.setWidth(5) plot_widget.plot(self.df_dist.loc[:, "{0}_{1}".format(name, col)], pen=pen, name=str(col)) self.plot_widgets.append(plot_widget) tasks = [] for name, item in region_list: for col in range(col_n): df = self.df_region.loc[self.df_region.loc[:, col]==name, col] intervals = get_interval(df.index) for interval in intervals: start, end = interval data = { "startDate": start, "endDate": end, "taskName": col, "status": name } tasks.append(data) self.chord_diagram_dialog.setMatrix(matrix.tolist()) self.chord_diagram_dialog.setColors(self.trackingPathGroup.getColors()) self.timeline_diagram_dialog.setTasks(tasks) self.timeline_diagram_dialog.setColors(colors) self.chord_diagram_dialog.show() if len(region_list)!=0: self.timeline_diagram_dialog.show() self.relation_matrix = matrix self.savedFlag = False # self.saveCSVFile() def saveCSVFile(self, activated=False, filePath = None): if self.df is None or self.df_dist is None or self.df_region is None or self.relation_matrix is None: return dirctory = os.path.dirname(self.filePath) base_name = os.path.splitext(os.path.basename(self.filePath))[0] path = os.path.join(dirctory, '{0}-info.txt'.format(base_name)) filePath, _ = QFileDialog.getSaveFileName(None, 'Save TXT File', path, "TXT files (*.txt)") names = list(map(lambda x: x.data(Qt.UserRole), self.getCol(0))) items = [self.graphics_items[name] for name in names] point_list = list(filter(lambda x:type(x[1]) is FigureType.Point.value, zip(names, items))) if len(filePath) is not 0 and len(point_list) is not 0: logger.debug("Saving CSV file: {0}".format(filePath)) with open(filePath, "w") as fp: for name, item in point_list: fp.write('{0} : {1}'.format(name, item.getPoints())) for attr in ['distance', 'region']: path = os.path.join(dirctory, '{0}-{1}.csv'.format(base_name, attr)) filePath, _ = QFileDialog.getSaveFileName(None, 'Save CSV File', path, "CSV files (*.csv)") if len(filePath) is not 0: logger.debug("Saving CSV file: {0}".format(filePath)) if attr=='distance': self.df_dist.to_csv(filePath) elif attr=='region': self.df_region.to_csv(filePath) path = os.path.join(dirctory, '{0}-relation.csv'.format(base_name)) filePath, _ = QFileDialog.getSaveFileName(None, 'Save CSV File', path, "CSV files (*.csv)") if len(filePath) is not 0: pd.DataFrame(self.relation_matrix).to_csv(filePath) self.savedFlag = True def initialize(self): if self.df is None or not self.videoPlaybackWidget.isOpened(): return self.trackingPathGroup.setPoints(self.currentFrameNo) r = self.trackingPathGroup.autoAdjustRadius(self.cv_img.shape) self.radiusSpinBox.setValue(r) def evaluate(self, update=True): if self.df is None or not self.videoPlaybackWidget.isOpened(): pass else: if self.trackingPathGroup is not None: self.trackingPathGroup.setPoints(self.currentFrameNo)
class Ui_MainWindow(QMainWindow, Ui_MainWindowBase): def __init__(self): super(Ui_MainWindow, self).__init__() self.setupUi(self) self.videoPlaybackInit() self.imgInit() self.menuInit() self.fgbg = None self.filePath = None self.df = None self.df_dist = None self.df_region = None self.relation_matrix = None self.trackingPathGroup = None self.currentFrameNo = None self.graphics_items = {} self.plot_widgets = [] factory = QItemEditorFactory() factory.registerEditor(QVariant.Color, ColorListItemEditorCreator()) self.createGUI() self.chord_diagram_dialog = ChordDiagramDialog(self) self.timeline_diagram_dialog = TimelineDiagramDialog(self) self.savedFlag = True # dialog = TimelineDiagramDialog(self) # dialog.show() def createGUI(self): colorEditorFactory = QItemEditorFactory() colorEditorFactory.registerEditor(QVariant.Color, ColorListItemEditorCreator()) colorEditorDelegate = QStyledItemDelegate(self) colorEditorDelegate.setItemEditorFactory(colorEditorFactory) figureEditorFactory = QItemEditorFactory() figureEditorFactory.registerEditor(QVariant.String, FigureListItemEditorCreator()) figureEditorDelegate = QStyledItemDelegate(self) figureEditorDelegate.setItemEditorFactory(figureEditorFactory) self.regionTableWidget.cellChanged.connect( self.regionTableWidgetCellChanged) self.regionTableWidget.setColumnCount(3) self.regionTableWidget.setItemDelegateForColumn(1, colorEditorDelegate) self.regionTableWidget.setItemDelegateForColumn( 2, figureEditorDelegate) self.regionTableWidget.setHorizontalHeaderLabels( ["Name", "Color", "Type"]) self.regionTableWidget.verticalHeader().setVisible(False) self.regionTableWidget.resize(150, 50) qApp = QtWidgets.qApp self.upRegionButton.setIcon(qApp.style().standardIcon( QStyle.SP_ArrowUp)) self.downRegionButton.setIcon(qApp.style().standardIcon( QStyle.SP_ArrowDown)) self.addRegionButton.clicked.connect(self.addRegionButtonClicked) self.removeRegionButton.clicked.connect(self.removeRegionButtonClicked) self.upRegionButton.clicked.connect(self.upRegionButtonClicked) self.downRegionButton.clicked.connect(self.downRegionButtonClicked) self.radiusSpinBox.valueChanged.connect(self.radiusSpinBoxValueChanged) def radiusSpinBoxValueChanged(self, i): if self.trackingPathGroup is not None: self.trackingPathGroup.setRadius(i) self.updateInputGraphicsView() def addRegionButtonClicked(self): if not self.videoPlaybackWidget.isOpened(): return name_num = self.regionTableWidget.rowCount() for i, name in enumerate( map(lambda x: x.data(Qt.DisplayRole), self.getCol(0))): try: val = int(name) except: continue name_num = max(name_num, val + 1) self.addRow(str(name_num), QColor('red'), FigureType.Recutangular) def removeRegionButtonClicked(self): if not self.videoPlaybackWidget.isOpened(): return if not len(self.regionTableWidget.selectedItems()) > 0: return selected_row = self.regionTableWidget.row( self.regionTableWidget.selectedItems()[0]) name_item = self.regionTableWidget.item(selected_row, 0) name = name_item.data(Qt.UserRole) item = self.graphics_items.pop(name) if item is not None: self.inputScene.removeItem(item) self.regionTableWidget.removeRow(selected_row) def upRegionButtonClicked(self): if not self.videoPlaybackWidget.isOpened(): return self.moveRow(True) def downRegionButtonClicked(self): if not self.videoPlaybackWidget.isOpened(): return self.moveRow(False) def addRow(self, name, color, figType): i = self.regionTableWidget.rowCount() self.regionTableWidget.insertRow(i) nameItem = QTableWidgetItem(name) nameItem.setData(Qt.UserRole, name) nameItem.setFlags(Qt.ItemIsEditable | Qt.ItemIsSelectable | Qt.ItemIsEnabled) colorItem = QTableWidgetItem() colorItem.setData(Qt.BackgroundRole, color) colorItem.setData(Qt.DisplayRole, color) figItem = QTableWidgetItem() figItem.setData(Qt.DisplayRole, figType.name) self.regionTableWidget.setItem(i, 0, nameItem) self.regionTableWidget.setItem(i, 1, colorItem) self.regionTableWidget.setItem(i, 2, figItem) self.regionTableWidget.resizeColumnToContents(0) self.regionTableWidget.horizontalHeader().setStretchLastSection(True) def regionTableWidgetCellChanged(self, row, col): changed_item = self.regionTableWidget.item(row, col) if col == 0: old_name = changed_item.data(Qt.UserRole) new_name = changed_item.data(Qt.DisplayRole) if old_name != new_name: for i, name in enumerate( map(lambda x: x.data(Qt.DisplayRole), self.getCol(0))): if i == row: continue if name == new_name: changed_item.setData(Qt.DisplayRole, old_name) return self.graphics_items[new_name] = self.graphics_items.pop( old_name) item = self.graphics_items[new_name] if item is not None: item.setObjectName(new_name) changed_item.setData(Qt.UserRole, new_name) if col == 1: try: bg_color = changed_item.data(Qt.BackgroundRole) disp_color = changed_item.data(Qt.DisplayRole) if disp_color.name() != bg_color.name(): bg_color.setNamedColor(disp_color.name()) changed_item.setData(Qt.BackgroundRole, bg_color) name_item = self.regionTableWidget.item(row, 0) name = name_item.data(Qt.DisplayRole) item = self.graphics_items[name] if item is not None: item.setColor(disp_color) except: pass if col == 2: old_type = changed_item.data(Qt.UserRole) new_type = FigureType[changed_item.data(Qt.DisplayRole)] if new_type is not old_type: name_item = self.regionTableWidget.item(row, 0) name = name_item.data(Qt.UserRole) item = self.getGraphicsItemFromInputScene(name) if item is not None: self.inputScene.removeItem(item) new_fig = new_type.value() new_fig.setObjectName(name) self.graphics_items[name] = new_fig if new_type is not FigureType.Point: new_fig.setZValue(1000 - 10 * row) else: new_fig.autoAdjustRadius(self.cv_img.shape) new_fig.setColor( self.regionTableWidget.item(row, 1).data(Qt.BackgroundRole)) self.inputScene.addItem(new_fig) changed_item.setData(Qt.UserRole, new_type) height, width, dim = self.cv_img.shape if new_type is FigureType.Point: array = np.array([0.5 * width, 0.5 * height]) elif new_type is FigureType.Polygon: array = [[0.1 * width, 0.1 * height], [0.9 * width, 0.1 * height], [0.9 * width, 0.9 * height], [0.1 * width, 0.9 * height]] else: array = [[0.1 * width, 0.1 * height], [0.9 * width, 0.9 * height]] new_fig.setPoints(array) self.updateInputGraphicsView() def moveRow(self, up): if not len(self.regionTableWidget.selectedItems()) > 0: return source_row = self.regionTableWidget.row( self.regionTableWidget.selectedItems()[0]) if up: dest_row = source_row - 1 else: dest_row = source_row + 1 if not (dest_row >= 0 and dest_row < self.regionTableWidget.rowCount()): return source_type = self.regionTableWidget.item(source_row, 2).data(Qt.UserRole) dest_type = self.regionTableWidget.item(dest_row, 2).data(Qt.UserRole) if source_type is not FigureType.Point and dest_type is not FigureType.Point: source_name = self.regionTableWidget.item(source_row, 0).data(Qt.DisplayRole) source_fig_item = self.graphics_items[source_name] source_z = source_fig_item.zValue() dest_name = self.regionTableWidget.item(dest_row, 0).data(Qt.DisplayRole) dest_fig_item = self.graphics_items[dest_name] dest_z = dest_fig_item.zValue() source_fig_item.setZValue(dest_z) dest_fig_item.setZValue(source_z) # take whole rows sourceItems = self.takeRow(source_row) destItems = self.takeRow(dest_row) # set back in reverse order self.setRow(source_row, destItems) self.setRow(dest_row, sourceItems) def takeRow(self, row): rowItems = [] for col in range(self.regionTableWidget.columnCount()): rowItems.append(self.regionTableWidget.takeItem(row, col)) return rowItems def getCol(self, col): colItems = [] for row in range(self.regionTableWidget.rowCount()): colItems.append(self.regionTableWidget.item(row, col)) return colItems def setRow(self, row, rowItems): for col in range(self.regionTableWidget.columnCount()): self.regionTableWidget.setItem(row, col, rowItems[col]) def getGraphicsItemFromInputScene(self, name): for item in self.inputScene.items(): #QGraphicsObjectをSceneから取り出そうとすると, #親クラスであるQGraphicsItem(QPixmapGraphicsItem)にダウンキャスト #されて返ってくるためtryが必要. try: if name == item.objectName(): return item except: pass return None def dragEnterEvent(self, event): event.acceptProposedAction() def dropEvent(self, event): mime = event.mimeData() if mime.hasUrls(): urls = mime.urls() if len(urls) > 0: self.processDropedFile(urls[0].toLocalFile()) event.acceptProposedAction() def closeEvent(self, event): if self.df is None or self.savedFlag: return quit_msg = "Data is not saved.\nAre you sure you want to exit the program?" reply = QtWidgets.QMessageBox.question(self, 'Warning', quit_msg, QtWidgets.QMessageBox.Yes, QtWidgets.QMessageBox.No) if reply == QtWidgets.QMessageBox.Yes: self.closeDialog() event.accept() else: event.ignore() def openTrackingPathColorSelectorDialog(self, activated=False): if self.trackingPathGroup is not None: self.trackingPathGroup.openColorSelectorDialog(self) def processDropedFile(self, filePath): root, ext = os.path.splitext(filePath) if ext == ".csv": self.openCSVFile(filePath=filePath) elif self.openVideoFile(filePath=filePath): return def videoPlaybackInit(self): self.videoPlaybackWidget.hide() self.videoPlaybackWidget.frameChanged.connect(self.setFrame, type=Qt.QueuedConnection) def setFrame(self, frame, frameNo): if frame is not None: self.cv_img = frame self.currentFrameNo = frameNo self.updateInputGraphicsView() self.evaluate() def imgInit(self): self.cv_img = cv2.imread( os.path.join(sampleDataPath, "color_filter_test.png")) self.inputScene = QGraphicsScene() self.inputGraphicsView.setScene(self.inputScene) self.inputGraphicsView.resizeEvent = self.inputGraphicsViewResized qimg = misc.cvMatToQImage(self.cv_img) self.inputPixMap = QPixmap.fromImage(qimg) self.inputPixMapItem = QGraphicsPixmapItem(self.inputPixMap) self.inputScene.addItem(self.inputPixMapItem) def menuInit(self): self.actionOpenVideo.triggered.connect(self.openVideoFile) self.actionOpenCSVFile.triggered.connect(self.openCSVFile) self.actionSaveCSVFile.triggered.connect(self.saveCSVFile) self.actionCalculate.triggered.connect(self.process) self.actionTrackingPathColor.triggered.connect( self.openTrackingPathColorSelectorDialog) def openVideoFile(self, activated=False, filePath=None): if filePath is None: filePath, _ = QFileDialog.getOpenFileName(None, 'Open Video File', userDir) if len(filePath) is not 0: self.filePath = filePath self.fgbg = None ret = self.videoPlaybackWidget.openVideo(filePath) if ret == False: return False self.videoPlaybackWidget.show() self.cv_img = self.videoPlaybackWidget.getCurrentFrame() self.initialize() return True def openCSVFile(self, activated=False, filePath=None): if filePath is None: filePath, _ = QFileDialog.getOpenFileName(None, 'Open CSV File', userDir, 'CSV files (*.csv)') if len(filePath) is not 0: self.filePath = filePath self.df = pd.read_csv(filePath, index_col=0) if self.trackingPathGroup is not None: self.inputScene.removeItem(self.trackingPathGroup) self.trackingPathGroup = TrackingPathGroup() self.trackingPathGroup.setRect(self.inputScene.sceneRect()) self.inputScene.addItem(self.trackingPathGroup) self.trackingPathGroup.setDataFrame(self.df) self.initialize() def updateInputGraphicsView(self): self.inputScene.removeItem(self.inputPixMapItem) qimg = misc.cvMatToQImage(self.cv_img) self.inputPixMap = QPixmap.fromImage(qimg) rect = QtCore.QRectF(self.inputPixMap.rect()) self.inputScene.setSceneRect(rect) self.inputPixMapItem = QGraphicsPixmapItem(self.inputPixMap) self.inputScene.addItem(self.inputPixMapItem) self.inputGraphicsView.viewport().update() self.inputGraphicsViewResized() def inputGraphicsViewResized(self, event=None): self.inputGraphicsView.fitInView( QtCore.QRectF(self.inputPixMap.rect()), QtCore.Qt.KeepAspectRatio) def closeDialog(self): self.chord_diagram_dialog.hide() self.timeline_diagram_dialog.hide() for plot_widget in self.plot_widgets: plot_widget.window().close() self.plot_widgets.clear() def process(self, activated=False): # TODO: 分割. # if self.df is None or len(self.getCol(0))==0: # return if self.df is None: return self.closeDialog() names = list(map(lambda x: x.data(Qt.UserRole), self.getCol(0))) items = [self.graphics_items[name] for name in names] colors = {('no' + name): color.data(Qt.BackgroundRole) for name, color in zip(names, self.getCol(1))} region_list = list( filter(lambda x: type(x[1]) is not FigureType.Point.value, zip(names, items))) point_list = list( filter(lambda x: type(x[1]) is FigureType.Point.value, zip(names, items))) col_n = int(len(self.df.columns) / 2) columns = [ "{0}_{1}".format(name, col) for col in range(col_n) for name, _ in point_list ] df = self.df.copy() df.columns = list(range(2 * col_n)) self.df_region = pd.DataFrame(index=df.index, columns=range(col_n)) self.df_dist = pd.DataFrame(index=df.index, columns=columns) progress = QProgressDialog("Running...", "Abort", 0, len(df.index), self) progress.setWindowModality(Qt.WindowModal) interactions = [0 for i in range(int(nCr(col_n, 2)))] radius = 2 * self.trackingPathGroup.getRadius() # radius = 100 for i, row in enumerate(df.index): progress.setValue(i) if progress.wasCanceled(): break pts = [] for col in range(col_n): pt = np.array(df.loc[row, 2 * col:2 * col + 1]) pts.append(pt) for name, item in point_list: self.df_dist.loc[ row, "{0}_{1}".format(name, col)] = item.distance(pt) for name, item in region_list: # TODO: 領域がかぶったときの挙動がアヤシい.要チェック. if item.includes(pt): self.df_region.loc[row, col] = name break for count, (p1, p2) in enumerate(itertools.combinations(pts, 2)): d = np.linalg.norm(p1 - p2) if d <= radius: interactions[count] += 1 progress.setValue(len(df.index)) matrix = np.zeros((col_n, col_n)) for pos, (i, j) in enumerate(itertools.combinations(range(col_n), 2)): matrix[i, j] = interactions[pos] matrix[j, i] = interactions[pos] for name, item in point_list: plot_widget = pg.plot(title="Point: " + name) plot_widget.addLegend() plot_item = plot_widget.getPlotItem() bottom_axis = plot_item.getAxis("bottom") bottom_axis.setLabel("# of Frame") left_axis = plot_item.getAxis("left") left_axis.setLabel("Distance [pixel]") for col, color in zip(range(col_n), self.trackingPathGroup.getColors()): pen = QPen(QColor(color)) pen.setWidth(5) plot_widget.plot(self.df_dist.loc[:, "{0}_{1}".format(name, col)], pen=pen, name=str(col)) self.plot_widgets.append(plot_widget) tasks = [] for name, item in region_list: for col in range(col_n): df = self.df_region.loc[self.df_region.loc[:, col] == name, col] intervals = get_interval(df.index) for interval in intervals: start, end = interval data = { "startDate": start, "endDate": end, "taskName": col, "status": name } tasks.append(data) self.chord_diagram_dialog.setMatrix(matrix.tolist()) self.chord_diagram_dialog.setColors(self.trackingPathGroup.getColors()) self.timeline_diagram_dialog.setTasks(tasks) self.timeline_diagram_dialog.setColors(colors) self.chord_diagram_dialog.show() if len(region_list) != 0: self.timeline_diagram_dialog.show() self.relation_matrix = matrix self.savedFlag = False # self.saveCSVFile() def saveCSVFile(self, activated=False, filePath=None): if self.df is None or self.df_dist is None or self.df_region is None or self.relation_matrix is None: return dirctory = os.path.dirname(self.filePath) base_name = os.path.splitext(os.path.basename(self.filePath))[0] path = os.path.join(dirctory, '{0}-info.txt'.format(base_name)) filePath, _ = QFileDialog.getSaveFileName(None, 'Save TXT File', path, "TXT files (*.txt)") names = list(map(lambda x: x.data(Qt.UserRole), self.getCol(0))) items = [self.graphics_items[name] for name in names] point_list = list( filter(lambda x: type(x[1]) is FigureType.Point.value, zip(names, items))) if len(filePath) is not 0 and len(point_list) is not 0: logger.debug("Saving CSV file: {0}".format(filePath)) with open(filePath, "w") as fp: for name, item in point_list: fp.write('{0} : {1}'.format(name, item.getPoints())) for attr in ['distance', 'region']: path = os.path.join(dirctory, '{0}-{1}.csv'.format(base_name, attr)) filePath, _ = QFileDialog.getSaveFileName(None, 'Save CSV File', path, "CSV files (*.csv)") if len(filePath) is not 0: logger.debug("Saving CSV file: {0}".format(filePath)) if attr == 'distance': self.df_dist.to_csv(filePath) elif attr == 'region': self.df_region.to_csv(filePath) path = os.path.join(dirctory, '{0}-relation.csv'.format(base_name)) filePath, _ = QFileDialog.getSaveFileName(None, 'Save CSV File', path, "CSV files (*.csv)") if len(filePath) is not 0: pd.DataFrame(self.relation_matrix).to_csv(filePath) self.savedFlag = True def initialize(self): if self.df is None or not self.videoPlaybackWidget.isOpened(): return self.trackingPathGroup.setPoints(self.currentFrameNo) r = self.trackingPathGroup.autoAdjustRadius(self.cv_img.shape) self.radiusSpinBox.setValue(r) def evaluate(self, update=True): if self.df is None or not self.videoPlaybackWidget.isOpened(): pass else: if self.trackingPathGroup is not None: self.trackingPathGroup.setPoints(self.currentFrameNo)
class Ui_MainWindow(QtWidgets.QMainWindow, Ui_MainWindowBase): def __init__(self): super(Ui_MainWindow, self).__init__() self.setupUi(self) self.videoPlaybackInit() self.imgInit() self.menuInit() self.df = {} self.trackingPathGroup = None self.movableArrowGroup = None self.line_data_dict = {} self.line_item_dict = {} self.file_name_dict = {} self.currentFrameNo = 0 self.colors = None self.overlayCheckBox.stateChanged.connect(self.overlayCheckBoxStateChanged) self.radiusSpinBox.valueChanged.connect(self.radiusSpinBoxValueChanged) self.frameNoSpinBox.valueChanged.connect(self.frameNoSpinBoxValueChanged) self.markDeltaSpinBox.valueChanged.connect(self.markDeltaSpinBoxValueChanged) def overlayCheckBoxStateChanged(self, s): if self.overlayCheckBox.isChecked(): self.frameBufferItemGroup.show() else: self.frameBufferItemGroup.hide() self.updateInputGraphicsView() def markDeltaSpinBoxValueChanged(self, value): if self.trackingPathGroup is not None: self.trackingPathGroup.setMarkDelta(self.markDeltaSpinBox.value()) self.updateInputGraphicsView() def radiusSpinBoxValueChanged(self, value): if self.trackingPathGroup is not None: self.trackingPathGroup.setRadius(self.radiusSpinBox.value()) self.updateInputGraphicsView() def frameNoSpinBoxValueChanged(self, value): if self.trackingPathGroup is not None: self.trackingPathGroup.setOverlayFrameNo(self.frameNoSpinBox.value()) self.updateInputGraphicsView() def dragEnterEvent(self,event): event.acceptProposedAction() def dropEvent(self,event): # event.setDropAction(QtCore.Qt.MoveAction) mime = event.mimeData() if mime.hasUrls(): urls = mime.urls() if len(urls) > 0: self.processDropedFile(urls[0].toLocalFile()) event.acceptProposedAction() def processDropedFile(self,filePath): root,ext = os.path.splitext(filePath) if ext == ".filter": # Read Filter self.openFilterFile(filePath=filePath) return elif ext == ".csv": self.openCSVFile(filePath=filePath) elif ext == ".json": self.openJSONFile(filePath=filePath) elif ext == ".color": self.openColorFile(filePath=filePath) elif self.openImageFile(filePath=filePath): return elif self.openVideoFile(filePath=filePath): return def videoPlaybackInit(self): self.videoPlaybackWidget.hide() self.videoPlaybackWidget.frameChanged.connect(self.setFrame, Qt.QueuedConnection) def setFrame(self, frame, frameNo): if frame is not None: self.cv_img = frame self.currentFrameNo = frameNo self.updateInputGraphicsView() self.evaluate() def imgInit(self): self.cv_img = cv2.imread(os.path.join(sampleDataPath,"color_filter_test.png")) self.frameBuffer = Queue() self.frameBufferItemGroup = QGraphicsItemGroup() self.frameBufferItemGroup.hide() self.inputPixmapRenderScene = QGraphicsScene() self.inputPixmapRenderScene.addItem(self.frameBufferItemGroup) self.inputScene = QGraphicsScene() self.inputGraphicsView.setScene(self.inputScene) self.inputGraphicsView.resizeEvent = self.graphicsViewResized # self.inputScene.addItem(self.frameBufferItemGroup) qimg = misc.cvMatToQImage(self.cv_img) self.inputPixmap = QPixmap.fromImage(qimg) self.inputPixmapItem = QGraphicsPixmapItem(self.inputPixmap) self.inputScene.addItem(self.inputPixmapItem) self.rubberBand = QtWidgets.QRubberBand(QtWidgets.QRubberBand.Rectangle, self.inputGraphicsView) self.inputGraphicsView.mousePressEvent = self.inputGraphicsViewMousePressEvent self.inputGraphicsView.mouseMoveEvent = self.inputGraphicsViewMouseMoveEvent self.inputGraphicsView.mouseReleaseEvent = self.inputGraphicsViewMouseReleaseEvent self.inputGraphicsView.viewport().installEventFilter(self) self.inputGraphicsView.setMouseTracking(True) self.overlayScene = QGraphicsScene() self.inputGraphicsView.setOverlayScene(self.overlayScene) self.zoomedGraphicsView.setScene(self.inputScene) self.zoomedGraphicsView.setOverlayScene(self.overlayScene) def inputGraphicsViewMousePressEvent(self, event): self.origin = QPoint(event.pos()) self.rubberBand.setGeometry( QtCore.QRect(self.origin, QtCore.QSize())) self.rubberBand.show() # Comment out to permit the view for sending the event to the child scene. # QGraphicsView.mousePressEvent(self.inputGraphicsView, event) def inputGraphicsViewMouseMoveEvent(self, event): if self.rubberBand.isVisible(): self.rubberBand.setGeometry( QtCore.QRect(self.origin, event.pos()).normalized()) # Comment out to permit the view for sending the event to the child scene. # QGraphicsView.mouseMoveEvent(self.inputGraphicsView, event) def inputGraphicsViewMouseReleaseEvent(self, event): if self.rubberBand.isVisible(): self.rubberBand.hide() rect = self.rubberBand.geometry() sceneRect = self.inputGraphicsView.mapToScene(rect).boundingRect() self.zoomedGraphicsView.fitInView(QRectF(sceneRect)) self.zoomedGraphicsView.viewport().update() # Comment out to permit the view for sending the event to the child scene. self.inputGraphicsView.viewport().update() # QGraphicsView.mouseReleaseEvent(self.inputGraphicsView, event) def menuInit(self): self.actionSaveDataFiles.triggered.connect(self.saveDataFiles) self.actionOpenCSVFile.triggered.connect(self.openCSVFile) self.actionOpenCSVFile.triggered.connect(self.openJSONFile) self.actionOpenCSVFile.triggered.connect(self.openJSONFile) self.actionPath.triggered.connect(self.actionPathTriggered) self.actionCircle.triggered.connect(self.actionCircleTriggered) self.actionIntervalMark.triggered.connect(self.actionIntervalMarkTriggered) self.actionShape.triggered.connect(self.actionShapeTriggered) self.actionSkeleton.triggered.connect(self.actionSkeletonTriggered) self.actionArrow.triggered.connect(self.actionArrowTriggered) self.actionChangeOrderOfNum.triggered.connect(self.actionChangeOrderOfNumTriggered) self.actionTrackingPathColor.triggered.connect(self.openTrackingPathColorSelectorDialog) def actionPathTriggered(self, checked): if self.trackingPathGroup is not None: self.trackingPathGroup.setDrawLine(checked) if not checked or self.actionIntervalMark.isChecked(): self.trackingPathGroup.setDrawMarkItem(checked) self.updateInputGraphicsView() def actionCircleTriggered(self, checked): if self.trackingPathGroup is not None: self.trackingPathGroup.setDrawItem(checked) self.updateInputGraphicsView() def actionIntervalMarkTriggered(self, checked): if self.trackingPathGroup is not None: self.trackingPathGroup.setDrawMarkItem(checked) self.updateInputGraphicsView() def actionShapeTriggered(self, checked): if 'shape' in self.line_item_dict.keys(): line_item = self.line_item_dict['shape'] if checked: line_item.show() else: line_item.hide() def actionSkeletonTriggered(self, checked): if 'skeleton' in self.line_item_dict.keys(): line_item = self.line_item_dict['skeleton'] if checked: line_item.show() else: line_item.hide() def actionArrowTriggered(self, checked): if self.movableArrowGroup is not None: if checked: self.movableArrowGroup.show() else: self.movableArrowGroup.hide() def actionChangeOrderOfNumTriggered(self, checked): if len(self.df.keys())!=0 or len(self.line_data_dict.keys())!=0: self.videoPlaybackWidget.stop() dialog = DataSwapDialog(self) dialog.setWindowModality(Qt.WindowModal) dialog.setData(self.df, self.line_data_dict) dialog.swapAccepted.connect(self.evaluate) res = dialog.exec() def openTrackingPathColorSelectorDialog(self, activated=False): if self.trackingPathGroup is not None: self.trackingPathGroup.openColorSelectorDialog(self) def openVideoFile(self, activated=False, filePath = None): if filePath is None: filePath, _ = QFileDialog.getOpenFileName(None, 'Open Video File', userDir) if len(filePath) is not 0: self.filePath = filePath ret = self.videoPlaybackWidget.openVideo(filePath) if ret == False: return False self.videoPlaybackWidget.show() self.cv_img = self.videoPlaybackWidget.getCurrentFrame() self.initialize() return True else: return False def openImageFile(self, activated=False, filePath = None): if filePath == None: filePath, _ = QFileDialog.getOpenFileName(None, 'Open Image File', userDir) if len(filePath) is not 0: self.filePath = filePath img = cv2.imread(filePath) if img is None: return False self.cv_img = img self.videoPlaybackWidget.hide() self.updateInputGraphicsView() self.evaluate() return True else: return False def openCSVFile(self, activated=False, filePath=None): if filePath is None: filePath, _ = QFileDialog.getOpenFileName(None, 'Open CSV File', userDir, 'CSV files (*.csv)') if len(filePath) is not 0: df = pd.read_csv(filePath, index_col=0) name = df.index.name if name is None: name = 'position' self.df[name] = df self.file_name_dict[name] = filePath if name is None or name=='position': if self.trackingPathGroup is not None: self.inputScene.removeItem(self.trackingPathGroup) self.trackingPathGroup = TrackingPathGroup() self.trackingPathGroup.setRect(self.inputScene.sceneRect()) self.inputScene.addItem(self.trackingPathGroup) if not self.actionPath.isChecked(): self.trackingPathGroup.setDrawLine(False) if not self.actionCircle.isChecked(): self.trackingPathGroup.setDrawItem(False) if not self.actionIntervalMark.isChecked(): self.trackingPathGroup.setDrawMarkItem(False) self.trackingPathGroup.setDataFrame(self.df['position']) elif name=='arrow': if self.movableArrowGroup is not None: self.inputScene.removeItem(self.movableArrowGroup) self.movableArrowGroup = MovableArrowGroup() self.inputScene.addItem(self.movableArrowGroup) self.movableArrowGroup.edited.connect(self.arrowEdited) if not self.actionArrow.isChecked(): self.movableArrowGroup.hide() if 'arrow' in self.df.keys() and 'position' in self.df.keys(): self.movableArrowGroup.setDataFrame(self.df['arrow'], self.df['position']) self.initialize() def openColorFile(self, activated=False, filePath=None): if filePath is None: filePath, _ = QFileDialog.getOpenFileName(None, 'Open Color File', userDir, 'Color files (*.color)') if len(filePath) is not 0: self.colors = pd.read_csv(filePath, index_col=0).as_matrix().tolist() self.colors = [QColor(*rgb) for rgb in self.colors] self.setColorsToGraphicsObjects() def openJSONFile(self, activated=False, filePath=None): if filePath is None: filePath, _ = QFileDialog.getOpenFileName(None, 'Open JSON File', userDir, 'JSON files (*.json)') if len(filePath) is not 0: with open(filePath) as f_p: data = json.load(f_p) name = data['name'] self.line_data_dict[name] = data self.file_name_dict[name] = filePath if name in self.line_item_dict.keys(): self.inputScene.removeItem(self.line_item_dict[name]) lines = MovableLineGroup() lines.setData(data) lines.setRect(self.inputScene.sceneRect()) if name=='shape' and not self.actionShape.isChecked(): lines.hide() if name=='skeleton' and not self.actionSkeleton.isChecked(): lines.hide() self.line_item_dict[name] = lines self.inputScene.addItem(lines) self.initialize() def saveDataFiles(self, activated=False, filePath = None): if len(self.df.keys())!=0: for k, v in self.df.items(): f_name, f_ext = os.path.splitext(self.file_name_dict[k]) candidate_file_path = '{0}-fixed{1}'.format(f_name, f_ext) filePath, _ = QFileDialog.getSaveFileName(None, 'Save CSV File', candidate_file_path, "CSV files (*.csv)") if len(filePath) is not 0: logger.debug("Saving CSV file: {0}".format(filePath)) df = v.copy() col_n = df.as_matrix().shape[1]/2 col_names = np.array([('x{0}'.format(i), 'y{0}'.format(i)) for i in range(int(round(col_n)))]).flatten() df.columns = pd.Index(col_names) df.to_csv(filePath) for k, v in self.line_data_dict.items(): f_name, f_ext = os.path.splitext(self.file_name_dict[k]) candidate_file_path = '{0}-fixed{1}'.format(f_name, f_ext) filePath, _ = QFileDialog.getSaveFileName(None, 'Save JSON File', candidate_file_path, "JSON files (*.json)") if len(filePath) is not 0: logger.debug("Saving JSON file: {0}".format(filePath)) with open(filePath, 'w') as f_p: json.dump(v, f_p) def updateInputGraphicsView(self): self.inputScene.removeItem(self.inputPixmapItem) qimg = misc.cvMatToQImage(self.cv_img) self.inputPixmap = QPixmap.fromImage(qimg) p = QPainter(self.inputPixmap) sourceRect = self.inputPixmapRenderScene.sceneRect() self.inputPixmapRenderScene.render(p, QRectF(sourceRect), QRectF(sourceRect), QtCore.Qt.IgnoreAspectRatio) self.inputPixmapItem = QGraphicsPixmapItem(self.inputPixmap) rect = QtCore.QRectF(self.inputPixmap.rect()) self.inputScene.setSceneRect(rect) self.inputScene.addItem(self.inputPixmapItem) self.inputGraphicsView.viewport().update() self.graphicsViewResized() def eventFilter(self, obj, event): if obj is self.inputGraphicsView.viewport() and event.type()==QEvent.Wheel: return True if event.type() == QEvent.KeyPress: if Qt.Key_Home <= event.key() <= Qt.Key_PageDown: self.videoPlaybackWidget.playbackSlider.keyPressEvent(event) return True return False def graphicsViewResized(self, event=None): self.inputGraphicsView.fitInView(QtCore.QRectF(self.inputPixmap.rect()), QtCore.Qt.KeepAspectRatio) def setColorsToGraphicsObjects(self): # FIXME: データセットと色リストのサイズ整合性チェックが必要 if self.colors is not None: if self.trackingPathGroup is not None: self.trackingPathGroup.setColors(self.colors) for k, v in self.line_item_dict.items(): v.setColors(self.colors) def initialize(self): if not self.videoPlaybackWidget.isOpened(): return if self.trackingPathGroup is not None: r = self.trackingPathGroup.autoAdjustRadius(self.cv_img.shape) self.radiusSpinBox.setValue(r) self.trackingPathGroup.autoAdjustLineWidth(self.cv_img.shape) self.trackingPathGroup.setItemsAreMovable(True) if self.movableArrowGroup is not None: pass for k, v in self.line_item_dict.items(): v.autoAdjustLineWidth(self.cv_img.shape) v.autoAdjustMarkSize(self.cv_img.shape) self.setColorsToGraphicsObjects() self.evaluate() def evaluate(self): if not self.videoPlaybackWidget.isOpened(): return qimg = misc.cvMatToQImage(self.cv_img) pixmapItem = QGraphicsPixmapItem(QPixmap.fromImage(qimg)) pixmapItem.setOpacity(0.2) self.frameBuffer.put(pixmapItem) self.frameBufferItemGroup.addToGroup(pixmapItem) if self.frameBuffer.qsize() > 10: item = self.frameBuffer.get() self.frameBufferItemGroup.removeFromGroup(item) if self.trackingPathGroup is not None: self.trackingPathGroup.setPoints(self.currentFrameNo) if self.movableArrowGroup is not None: self.movableArrowGroup.setPositions(self.currentFrameNo) for k, v in self.line_item_dict.items(): v.setPolyline(self.currentFrameNo) @pyqtSlot(object) def arrowEdited(self, name): # TODO: 方向の再推定機能の実装 # quit_msg = "Arrow {} edited.\nRe-estimate the direction in following frames?".format(name) # reply = QtWidgets.QMessageBox.question( # self, # 'Question', # quit_msg, # QtWidgets.QMessageBox.Yes, # QtWidgets.QMessageBox.No # ) # # if reply == QtWidgets.QMessageBox.Yes: # pass # else: # pass pass
class Ui_MainWindow(QtWidgets.QMainWindow, Ui_MainWindowBase): def __init__(self): super(Ui_MainWindow, self).__init__() self.setupUi(self) self.videoPlaybackInit() self.imgInit() self.menuInit() self.df = {} self.trackingPathGroup = None self.movableArrowGroup = None self.line_data_dict = {} self.line_item_dict = {} self.file_name_dict = {} self.currentFrameNo = 0 self.colors = None self.overlayCheckBox.stateChanged.connect( self.overlayCheckBoxStateChanged) self.radiusSpinBox.valueChanged.connect(self.radiusSpinBoxValueChanged) self.frameNoSpinBox.valueChanged.connect( self.frameNoSpinBoxValueChanged) self.markDeltaSpinBox.valueChanged.connect( self.markDeltaSpinBoxValueChanged) def overlayCheckBoxStateChanged(self, s): if self.overlayCheckBox.isChecked(): self.frameBufferItemGroup.show() else: self.frameBufferItemGroup.hide() self.updateInputGraphicsView() def markDeltaSpinBoxValueChanged(self, value): if self.trackingPathGroup is not None: self.trackingPathGroup.setMarkDelta(self.markDeltaSpinBox.value()) self.updateInputGraphicsView() def radiusSpinBoxValueChanged(self, value): if self.trackingPathGroup is not None: self.trackingPathGroup.setRadius(self.radiusSpinBox.value()) self.updateInputGraphicsView() def frameNoSpinBoxValueChanged(self, value): if self.trackingPathGroup is not None: self.trackingPathGroup.setOverlayFrameNo( self.frameNoSpinBox.value()) self.updateInputGraphicsView() def dragEnterEvent(self, event): event.acceptProposedAction() def dropEvent(self, event): # event.setDropAction(QtCore.Qt.MoveAction) mime = event.mimeData() if mime.hasUrls(): urls = mime.urls() if len(urls) > 0: self.processDropedFile(urls[0].toLocalFile()) event.acceptProposedAction() def processDropedFile(self, filePath): root, ext = os.path.splitext(filePath) if ext == ".filter": # Read Filter self.openFilterFile(filePath=filePath) return elif ext == ".csv": self.openCSVFile(filePath=filePath) elif ext == ".json": self.openJSONFile(filePath=filePath) elif ext == ".color": self.openColorFile(filePath=filePath) elif self.openImageFile(filePath=filePath): return elif self.openVideoFile(filePath=filePath): return def videoPlaybackInit(self): self.videoPlaybackWidget.hide() self.videoPlaybackWidget.frameChanged.connect(self.setFrame, Qt.QueuedConnection) def setFrame(self, frame, frameNo): if frame is not None: self.cv_img = frame self.currentFrameNo = frameNo self.updateInputGraphicsView() self.evaluate() def imgInit(self): self.cv_img = cv2.imread( os.path.join(sampleDataPath, "color_filter_test.png")) self.frameBuffer = Queue() self.frameBufferItemGroup = QGraphicsItemGroup() self.frameBufferItemGroup.hide() self.inputPixmapRenderScene = QGraphicsScene() self.inputPixmapRenderScene.addItem(self.frameBufferItemGroup) self.inputScene = QGraphicsScene() self.inputGraphicsView.setScene(self.inputScene) self.inputGraphicsView.resizeEvent = self.graphicsViewResized # self.inputScene.addItem(self.frameBufferItemGroup) qimg = misc.cvMatToQImage(self.cv_img) self.inputPixmap = QPixmap.fromImage(qimg) self.inputPixmapItem = QGraphicsPixmapItem(self.inputPixmap) self.inputScene.addItem(self.inputPixmapItem) self.rubberBand = QtWidgets.QRubberBand( QtWidgets.QRubberBand.Rectangle, self.inputGraphicsView) self.inputGraphicsView.mousePressEvent = self.inputGraphicsViewMousePressEvent self.inputGraphicsView.mouseMoveEvent = self.inputGraphicsViewMouseMoveEvent self.inputGraphicsView.mouseReleaseEvent = self.inputGraphicsViewMouseReleaseEvent self.inputGraphicsView.viewport().installEventFilter(self) self.inputGraphicsView.setMouseTracking(True) self.overlayScene = QGraphicsScene() self.inputGraphicsView.setOverlayScene(self.overlayScene) self.zoomedGraphicsView.setScene(self.inputScene) self.zoomedGraphicsView.setOverlayScene(self.overlayScene) def inputGraphicsViewMousePressEvent(self, event): self.origin = QPoint(event.pos()) self.rubberBand.setGeometry(QtCore.QRect(self.origin, QtCore.QSize())) self.rubberBand.show() # Comment out to permit the view for sending the event to the child scene. # QGraphicsView.mousePressEvent(self.inputGraphicsView, event) def inputGraphicsViewMouseMoveEvent(self, event): if self.rubberBand.isVisible(): self.rubberBand.setGeometry( QtCore.QRect(self.origin, event.pos()).normalized()) # Comment out to permit the view for sending the event to the child scene. # QGraphicsView.mouseMoveEvent(self.inputGraphicsView, event) def inputGraphicsViewMouseReleaseEvent(self, event): if self.rubberBand.isVisible(): self.rubberBand.hide() rect = self.rubberBand.geometry() sceneRect = self.inputGraphicsView.mapToScene(rect).boundingRect() self.zoomedGraphicsView.fitInView(QRectF(sceneRect)) self.zoomedGraphicsView.viewport().update() # Comment out to permit the view for sending the event to the child scene. self.inputGraphicsView.viewport().update() # QGraphicsView.mouseReleaseEvent(self.inputGraphicsView, event) def menuInit(self): self.actionSaveDataFiles.triggered.connect(self.saveDataFiles) self.actionOpenCSVFile.triggered.connect(self.openCSVFile) self.actionOpenCSVFile.triggered.connect(self.openJSONFile) self.actionOpenCSVFile.triggered.connect(self.openJSONFile) self.actionPath.triggered.connect(self.actionPathTriggered) self.actionCircle.triggered.connect(self.actionCircleTriggered) self.actionIntervalMark.triggered.connect( self.actionIntervalMarkTriggered) self.actionShape.triggered.connect(self.actionShapeTriggered) self.actionSkeleton.triggered.connect(self.actionSkeletonTriggered) self.actionArrow.triggered.connect(self.actionArrowTriggered) self.actionChangeOrderOfNum.triggered.connect( self.actionChangeOrderOfNumTriggered) self.actionTrackingPathColor.triggered.connect( self.openTrackingPathColorSelectorDialog) def actionPathTriggered(self, checked): if self.trackingPathGroup is not None: self.trackingPathGroup.setDrawLine(checked) if not checked or self.actionIntervalMark.isChecked(): self.trackingPathGroup.setDrawMarkItem(checked) self.updateInputGraphicsView() def actionCircleTriggered(self, checked): if self.trackingPathGroup is not None: self.trackingPathGroup.setDrawItem(checked) self.updateInputGraphicsView() def actionIntervalMarkTriggered(self, checked): if self.trackingPathGroup is not None: self.trackingPathGroup.setDrawMarkItem(checked) self.updateInputGraphicsView() def actionShapeTriggered(self, checked): if 'shape' in self.line_item_dict.keys(): line_item = self.line_item_dict['shape'] if checked: line_item.show() else: line_item.hide() def actionSkeletonTriggered(self, checked): if 'skeleton' in self.line_item_dict.keys(): line_item = self.line_item_dict['skeleton'] if checked: line_item.show() else: line_item.hide() def actionArrowTriggered(self, checked): if self.movableArrowGroup is not None: if checked: self.movableArrowGroup.show() else: self.movableArrowGroup.hide() def actionChangeOrderOfNumTriggered(self, checked): if len(self.df.keys()) != 0 or len(self.line_data_dict.keys()) != 0: self.videoPlaybackWidget.stop() dialog = DataSwapDialog(self) dialog.setWindowModality(Qt.WindowModal) dialog.setData(self.df, self.line_data_dict) dialog.swapAccepted.connect(self.evaluate) res = dialog.exec() def openTrackingPathColorSelectorDialog(self, activated=False): if self.trackingPathGroup is not None: self.trackingPathGroup.openColorSelectorDialog(self) def openVideoFile(self, activated=False, filePath=None): if filePath is None: filePath, _ = QFileDialog.getOpenFileName(None, 'Open Video File', userDir) if len(filePath) is not 0: self.filePath = filePath ret = self.videoPlaybackWidget.openVideo(filePath) if ret == False: return False self.videoPlaybackWidget.show() self.cv_img = self.videoPlaybackWidget.getCurrentFrame() self.initialize() return True else: return False def openImageFile(self, activated=False, filePath=None): if filePath == None: filePath, _ = QFileDialog.getOpenFileName(None, 'Open Image File', userDir) if len(filePath) is not 0: self.filePath = filePath img = cv2.imread(filePath) if img is None: return False self.cv_img = img self.videoPlaybackWidget.hide() self.updateInputGraphicsView() self.evaluate() return True else: return False def openCSVFile(self, activated=False, filePath=None): if filePath is None: filePath, _ = QFileDialog.getOpenFileName(None, 'Open CSV File', userDir, 'CSV files (*.csv)') if len(filePath) is not 0: df = pd.read_csv(filePath, index_col=0) name = df.index.name if name is None: name = 'position' self.df[name] = df self.file_name_dict[name] = filePath if name is None or name == 'position': if self.trackingPathGroup is not None: self.inputScene.removeItem(self.trackingPathGroup) self.trackingPathGroup = TrackingPathGroup() self.trackingPathGroup.setRect(self.inputScene.sceneRect()) self.inputScene.addItem(self.trackingPathGroup) self.trackingPathGroup.setDrawLine(self.actionPath.isChecked()) self.trackingPathGroup.setDrawItem( self.actionCircle.isChecked()) self.trackingPathGroup.setDrawMarkItem( self.actionIntervalMark.isChecked()) shape = self.df['position'].shape self.num_items = int(shape[1] / 2) index = (np.repeat(range(self.num_items), 2).tolist(), [0, 1] * self.num_items) self.df['position'].columns = pd.MultiIndex.from_tuples( tuple(zip(*index))) self.trackingPathGroup.setDataFrame(self.df['position']) delta = self.df['position'].index[1] - self.df[ 'position'].index[0] self.videoPlaybackWidget.setPlaybackDelta(delta) self.videoPlaybackWidget.setMaxTickableFrameNo( self.df['position'].index[-1]) elif name == 'arrow': if self.movableArrowGroup is not None: self.inputScene.removeItem(self.movableArrowGroup) self.movableArrowGroup = MovableArrowGroup() self.inputScene.addItem(self.movableArrowGroup) self.movableArrowGroup.edited.connect(self.arrowEdited) if not self.actionArrow.isChecked(): self.movableArrowGroup.hide() if 'arrow' in self.df.keys() and 'position' in self.df.keys(): self.movableArrowGroup.setDataFrame(self.df['arrow'], self.df['position']) self.initialize() def openColorFile(self, activated=False, filePath=None): if filePath is None: filePath, _ = QFileDialog.getOpenFileName(None, 'Open Color File', userDir, 'Color files (*.color)') if len(filePath) is not 0: self.colors = pd.read_csv(filePath, index_col=0).values.tolist() self.colors = [QColor(*rgb) for rgb in self.colors] self.setColorsToGraphicsObjects() def openJSONFile(self, activated=False, filePath=None): if filePath is None: filePath, _ = QFileDialog.getOpenFileName(None, 'Open JSON File', userDir, 'JSON files (*.json)') if len(filePath) is not 0: with open(filePath) as f_p: data = json.load(f_p) name = data['name'] self.line_data_dict[name] = data self.file_name_dict[name] = filePath if name in self.line_item_dict.keys(): self.inputScene.removeItem(self.line_item_dict[name]) lines = MovableLineGroup() lines.setData(data) lines.setRect(self.inputScene.sceneRect()) if name == 'shape' and not self.actionShape.isChecked(): lines.hide() if name == 'skeleton' and not self.actionSkeleton.isChecked(): lines.hide() self.line_item_dict[name] = lines self.inputScene.addItem(lines) self.initialize() def saveDataFiles(self, activated=False, filePath=None): if len(self.df.keys()) != 0: for k, v in self.df.items(): f_name, f_ext = os.path.splitext(self.file_name_dict[k]) candidate_file_path = '{0}-fixed{1}'.format(f_name, f_ext) filePath, _ = QFileDialog.getSaveFileName( None, 'Save CSV File', candidate_file_path, "CSV files (*.csv)") if len(filePath) is not 0: logger.debug("Saving CSV file: {0}".format(filePath)) df = v.copy() col_n = df.values.shape[1] / 2 col_names = np.array([('x{0}'.format(i), 'y{0}'.format(i)) for i in range(int(round(col_n))) ]).flatten() df.columns = pd.Index(col_names) df.index.name = k df.to_csv(filePath) for k, v in self.line_data_dict.items(): f_name, f_ext = os.path.splitext(self.file_name_dict[k]) candidate_file_path = '{0}-fixed{1}'.format(f_name, f_ext) filePath, _ = QFileDialog.getSaveFileName(None, 'Save JSON File', candidate_file_path, "JSON files (*.json)") if len(filePath) is not 0: logger.debug("Saving JSON file: {0}".format(filePath)) with open(filePath, 'w') as f_p: json.dump(v, f_p) def updateInputGraphicsView(self): self.inputScene.removeItem(self.inputPixmapItem) qimg = misc.cvMatToQImage(self.cv_img) self.inputPixmap = QPixmap.fromImage(qimg) p = QPainter(self.inputPixmap) sourceRect = self.inputPixmapRenderScene.sceneRect() self.inputPixmapRenderScene.render(p, QRectF(sourceRect), QRectF(sourceRect), QtCore.Qt.IgnoreAspectRatio) self.inputPixmapItem = QGraphicsPixmapItem(self.inputPixmap) rect = QtCore.QRectF(self.inputPixmap.rect()) self.inputScene.setSceneRect(rect) self.inputScene.addItem(self.inputPixmapItem) self.inputGraphicsView.viewport().update() self.graphicsViewResized() def eventFilter(self, obj, event): if event.type() == QEvent.Wheel: self.videoPlaybackWidget.wheelEvent(event) return True if event.type() == QEvent.KeyPress: if Qt.Key_Home <= event.key() <= Qt.Key_PageDown: self.videoPlaybackWidget.keyPressEvent(event) return True return False def graphicsViewResized(self, event=None): self.inputGraphicsView.fitInView( QtCore.QRectF(self.inputPixmap.rect()), QtCore.Qt.KeepAspectRatio) def setColorsToGraphicsObjects(self): # FIXME: データセットと色リストのサイズ整合性チェックが必要 if self.colors is not None: if self.trackingPathGroup is not None: self.trackingPathGroup.setColors(self.colors) for k, v in self.line_item_dict.items(): v.setColors(self.colors) def initialize(self): if not self.videoPlaybackWidget.isOpened(): return if self.trackingPathGroup is not None: r = self.trackingPathGroup.autoAdjustRadius(self.cv_img.shape) self.radiusSpinBox.setValue(r) self.trackingPathGroup.autoAdjustLineWidth(self.cv_img.shape) self.trackingPathGroup.setItemsAreMovable(True) if self.movableArrowGroup is not None: pass for k, v in self.line_item_dict.items(): v.autoAdjustLineWidth(self.cv_img.shape) v.autoAdjustMarkSize(self.cv_img.shape) self.setColorsToGraphicsObjects() self.evaluate() def evaluate(self): if not self.videoPlaybackWidget.isOpened(): return qimg = misc.cvMatToQImage(self.cv_img) pixmapItem = QGraphicsPixmapItem(QPixmap.fromImage(qimg)) pixmapItem.setOpacity(0.2) self.frameBuffer.put(pixmapItem) self.frameBufferItemGroup.addToGroup(pixmapItem) if self.frameBuffer.qsize() > 10: item = self.frameBuffer.get() self.frameBufferItemGroup.removeFromGroup(item) if self.trackingPathGroup is not None: self.trackingPathGroup.setPoints(self.currentFrameNo) if self.movableArrowGroup is not None: self.movableArrowGroup.setPositions(self.currentFrameNo) for k, v in self.line_item_dict.items(): v.setPolyline(self.currentFrameNo) @pyqtSlot(object) def arrowEdited(self, name): # TODO: 方向の再推定機能の実装 # quit_msg = "Arrow {} edited.\nRe-estimate the direction in following frames?".format(name) # reply = QtWidgets.QMessageBox.question( # self, # 'Question', # quit_msg, # QtWidgets.QMessageBox.Yes, # QtWidgets.QMessageBox.No # ) # # if reply == QtWidgets.QMessageBox.Yes: # pass # else: # pass pass