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()
Exemple #2
0
    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)

            shape = self.df.shape
            self.num_items = int(shape[1] / 2)
            index = (np.repeat(range(self.num_items),
                               2).tolist(), [0, 1] * self.num_items)
            self.df.columns = pd.MultiIndex.from_tuples(tuple(zip(*index)))

            self.trackingPathGroup.setDataFrame(self.df)

            delta = self.df.index[1] - self.df.index[0]
            self.videoPlaybackWidget.setPlaybackDelta(delta)
            self.videoPlaybackWidget.setMaxTickableFrameNo(self.df.index[-1])

            self.initialize()
Exemple #3
0
    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)

            shape = self.df.shape
            self.num_items = int(shape[1]/2)
            index = (np.repeat(range(self.num_items), 2).tolist(), [0,1]*self.num_items)
            self.df.columns = pd.MultiIndex.from_tuples(tuple(zip(*index)))

            self.trackingPathGroup.setDataFrame(self.df)

            delta = self.df.index[1] - self.df.index[0]
            self.videoPlaybackWidget.setPlaybackDelta(delta)
            self.videoPlaybackWidget.setMaxTickableFrameNo(self.df.index[-1])

            self.initialize()
Exemple #4
0
    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 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 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()
Exemple #7
0
    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()
Exemple #8
0
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)
    def initializeTrackingSystem(self):
        self.isInitialized = False
        if  not (self.videoPlaybackWidget.isOpened() and filterOperation is not None):
            return False

        if self.currentFrameNo != 0:
            ret, frame = self.videoPlaybackWidget.readFrame(0)
            self.cv_img = frame
            self.currentFrameNo = 0
            self.videoPlaybackWidget.setSliderValueWithoutSignal(0)

        self.filter = filterOperation(self.cv_img)
        self.filter.fgbg = self.filterIO.getBackgroundImg()
        self.filter.isInit = True

        try:
            tracking_n = self.stackedWidget.currentWidget().get_tracking_n()
            attrs = self.stackedWidget.currentWidget().get_attributes()
            attrs.keys()
        except Exception as e:
            msg = 'Tracking Lib. Tracking N or attributes Error:\n{}'.format(e)
            self.generateCriticalMessage(msg)
            return

        max_frame_pos = self.videoPlaybackWidget.getMaxFramePos()

        self.df = {}
        for k, t in attrs.items():
            if t is None:
                self.data_dict[k] = {}
                self.data_dict[k]['name'] = k
            else:
                tuples = []
                for i in range(tracking_n):
                    for v in t:
                        tuples.append((i, v))
                col = pd.MultiIndex.from_tuples(tuples)
                self.df[k] = pd.DataFrame(index=range(max_frame_pos+1), columns=col, dtype=np.float64).sort_index().sort_index(axis=1)
                self.df[k].index.name = k

        self.removeTrackingGraphicsItems()

        if 'position' in attrs:
            self.trackingPathGroup = TrackingPathGroup()
            self.trackingPathGroup.setRect(self.inputScene.sceneRect())
            if self.pathCheckBox.checkState()==Qt.Unchecked:
                self.trackingPathGroup.hide()

            self.inputScene.addItem(self.trackingPathGroup)
            self.trackingPathGroup.setDataFrame(self.df['position'])

            lw = self.trackingPathGroup.autoAdjustLineWidth(self.cv_img.shape)
            r = self.trackingPathGroup.autoAdjustRadius(self.cv_img.shape)
            self.lineWidthSpinBox.setValue(lw)
            self.radiusSpinBox.setValue(r)

            self.trackingPathGroup.setItemsAreMovable(True)

        if 'rect' in attrs:
            self.item_dict['rect'] = [QGraphicsRectItem() for i in range(tracking_n)]
            for rect_item in self.item_dict['rect']:
                rect_item.setZValue(1000)
                self.inputScene.addItem(rect_item)

        if 'arrow' in attrs:
            self.item_dict['arrow'] = [MovableArrow() for i in range(tracking_n)]
            for arrow_item in self.item_dict['arrow']:
                arrow_item.setZValue(900)
                if self.arrowCheckBox.checkState()==Qt.Unchecked:
                    arrow_item.hide()
                self.inputScene.addItem(arrow_item)

        if 'path' in attrs:
            self.item_dict['path'] = [QGraphicsPathItem() for i in range(tracking_n)]
            for path_item in self.item_dict['path']:
                path_item.setZValue(900)
                self.inputScene.addItem(path_item)

        if 'polygon' in attrs:
            self.item_dict['polygon'] = [QGraphicsPathItem() for i in range(tracking_n)]
            for path_item in self.item_dict['polygon']:
                path_item.setZValue(900)
                self.inputScene.addItem(path_item)

        self.videoPlaybackWidget.setMaxTickableFrameNo(0)
        # if self.currentFrameNo != 0:
        #     self.videoPlaybackWidget.moveToFrame(0)
        self.videoPlaybackWidget.setPlaybackDelta(self.playbackDeltaSpinBox.value())

        self.isInitialized = True
class Ui_MainWindow(QMainWindow, Ui_MainWindowBase):
    updateFrame = pyqtSignal()

    def __init__(self):
        super(Ui_MainWindow, self).__init__()
        self.setupUi(self)

        self.videoPlaybackInit()
        self.imgInit()
        self.menuInit()

        self.radiusSpinBox.valueChanged.connect(self.radiusSpinBoxValueChanged)
        self.lineWidthSpinBox.valueChanged.connect(self.lineWidthSpinBoxValueChanged)
        self.overlayFrameNoSpinBox.valueChanged.connect(self.overlayFrameNoSpinBoxValueChanged)
        self.stackedWidget.currentChanged.connect(self.stackedWidgetCurrentChanged)

        self.arrowCheckBox.stateChanged.connect(self.arrowCheckBoxStateChanged)
        self.pathCheckBox.stateChanged.connect(self.pathCheckBoxStateChanged)
        self.reverseArrowColorCheckBox.stateChanged.connect(self.reverseArrowColorCheckBoxStateChanged)

        self.opaqueCheckBox.stateChanged.connect(self.opaqueCheckBoxStateChanged)

        self.videoPlaybackWidget.setSignalSlotMode()
        self.updateFrame.connect(self.videoPlaybackWidget.videoPlayback)

        self.filter = None
        self.filterIO = None
        self.isInitialized = False
        self.item_dict = {}
        self.data_dict = {}
        self.trackingPathGroup = None
        self.df = {}
        self.inputPixmapItem = None
        self.cv_img = None
        self.filePath = None
        self.savedFlag = True

    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 len(self.df.keys())==0 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:
            event.accept()
        else:
            event.ignore()

    def processDropedFile(self,filePath):
        root,ext = os.path.splitext(filePath)
        if ext == ".filter":
            # Read Filter
            self.openFilterFile(filePath=filePath)
            return
        elif self.openVideoFile(filePath=filePath):
            return

    def arrowCheckBoxStateChanged(self, state):
        if 'arrow' not in self.item_dict.keys():
            return

        for arrow_item in self.item_dict['arrow']:
            if state==Qt.Unchecked:
                arrow_item.hide()
            if state==Qt.Checked:
                arrow_item.show()

        self.updateInputGraphicsView()

    def pathCheckBoxStateChanged(self, state):
        if self.trackingPathGroup is None:
            return

        if state==Qt.Unchecked:
            self.trackingPathGroup.hide()
        if state==Qt.Checked:
            self.trackingPathGroup.show()

        self.updateInputGraphicsView()

    def reverseArrowColorCheckBoxStateChanged(self, state):
        if 'arrow' not in self.item_dict.keys():
            return

        for arrow_item in self.item_dict['arrow']:
            if state==Qt.Unchecked:
                arrow_item.setColor([0,0,0])
            if state==Qt.Checked:
                arrow_item.setColor([255,255,255])

        self.updateInputGraphicsView()

    def opaqueCheckBoxStateChanged(self, state):
        if self.trackingPathGroup is None:
            return

        if state==Qt.Unchecked:
            self.trackingPathGroup.setOpacity(0.5)
        if state==Qt.Checked:
            self.trackingPathGroup.setOpacity(1.0)

        self.updateInputGraphicsView()

    def reset(self):
        self.videoPlaybackWidget.stop()
        self.videoPlaybackWidget.moveToFrame(0)

    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()
        return

    def imgInit(self):
        self.inputScene = QGraphicsScene()
        self.inputGraphicsView.setScene(self.inputScene)
        self.inputGraphicsView.resizeEvent = self.inputGraphicsViewResized

    def menuInit(self):
        self.actionOpenVideo.triggered.connect(self.openVideoFile)
        self.actionOpenFilterSetting.triggered.connect(self.openFilterFile)

        self.actionSaveCSVFile.triggered.connect(self.saveCSVFile)

        self.actionRunObjectTracking.triggered.connect(self.runObjectTracking)
        self.actionTrackingPathColor.triggered.connect(self.openTrackingPathColorSelectorDialog)

        self.menuAlgorithmsActionGroup = QActionGroup(self.menuAlgorithms)

        path_list = [[tracking_system_path, currentDirPath], ]
        if os.path.exists(user_defined_lib_path):
            path_list.append([user_defined_tracking_system_path, user_defined_lib_path])
        for system_path in path_list:
            for module_path in get_modules(system_path[0], system_path[1]):
                module_str = '.'.join(module_path)

                try:
                    module = importlib.import_module(module_str)

                    if not hasattr(module, 'Widget'):
                        continue

                    class_def = getattr(module, "Widget")
                    if not issubclass(class_def, QtWidgets.QWidget):
                        continue

                    widget = class_def(self.stackedWidget)
                    widget.reset.connect(self.resetDataframe)
                    widget.restart.connect(self.restartDataframe)
                    self.stackedWidget.addWidget(widget)

                    action = self.menuAlgorithms.addAction(widget.get_name())
                    action.triggered.connect(self.generateAlgorithmsMenuClicked(widget))
                    action.setCheckable(True)
                    action.setActionGroup(self.menuAlgorithmsActionGroup)

                    if len(self.menuAlgorithmsActionGroup.actions()) == 1:
                        action.setChecked(True)
                        self.algorithmSettingsGroupBox.setTitle(widget.get_name())

                except Exception as e:
                    if system_path[1] is user_defined_lib_path:
                        msg = 'Tracking Lib. Load Fail: {0}\n{1}'.format(module_str, e)
                        self.generateCriticalMessage(msg)
                    continue

    def openTrackingPathColorSelectorDialog(self, activated=False):
        if self.trackingPathGroup is not None:
            self.trackingPathGroup.openColorSelectorDialog(self)

    def generateAlgorithmsMenuClicked(self, widget):
        def action_triggered(activated=False):
            if widget is not self.stackedWidget.currentWidget():
                self.stackedWidget.setCurrentWidget(widget)
            else:
                pass
        return action_triggered

    def initializeEventDialog(self):
        quit_msg = "Data is not saved.\nAre you sure you want to reset?"
        reply = QtWidgets.QMessageBox.question(
                self,
                'Warning',
                quit_msg,
                QtWidgets.QMessageBox.Yes,
                QtWidgets.QMessageBox.No
                )

        if reply == QtWidgets.QMessageBox.Yes:
            return True
        else:
            return False

    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:
            if filterOperation is not None and self.videoPlaybackWidget.isOpened():
                if self.initializeEventDialog():
                    global filterOperation
                    filterOperation = None
                    self.removeTrackingGraphicsItems()
                    self.savedFlag = True
                else:
                    return
            self.filePath = filePath
            ret = self.videoPlaybackWidget.openVideo(filePath)
            if not ret:
                return False

            self.videoPlaybackWidget.show()

            self.cv_img = self.videoPlaybackWidget.getCurrentFrame()
            self.currentFrameNo = 0
            self.videoPlaybackWidget.setMaxTickableFrameNo(0)
            self.initializeTrackingSystem()

            return True
        else:
            return False

    def openFilterFile(self, activated=False, filePath = None):
        if filePath is None:
            filePath, _ = QFileDialog.getOpenFileName(None, 'Open Block File', userDir, "Block files (*.filter)")

        if len(filePath) is not 0:
            if filterOperation is not None and self.videoPlaybackWidget.isOpened():
                if self.initializeEventDialog():
                    self.videoPlaybackWidget.closeVideo()
                    self.videoPlaybackWidget.hide()
                    self.removeTrackingGraphicsItems()
                    self.inputScene.removeItem(self.inputPixmapItem)
                    self.savedFlag = True
                else:
                    return
            logger.debug("Open Filter file: {0}".format(filePath))

            self.filterIO = FilterIO(filePath)

            exec(self.filterIO.getFilterCode(), globals())

            self.filter = None

            self.initializeTrackingSystem()
            self.evaluate()

    def saveCSVFile(self, activated=False, filePath = None):
        if len(self.df.keys())!=0:
            dirctory = os.path.dirname(self.filePath)
            base_name = os.path.splitext(os.path.basename(self.filePath))[0]

            path = os.path.join(dirctory, '{0}-{1}.txt'.format(base_name, "info"))
            filePath, _ = QFileDialog.getSaveFileName(None, 'Save Info File', path, "TXT files (*.txt)")

            if len(filePath) is not 0:
                logger.debug("Saving Info file: {0}".format(filePath))

                with open(filePath, 'w') as fp:
                    fp.write(self.videoPlaybackWidget.getVideoInfo())

            for attr, df in self.df.items():
                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))

                    row_max = self.currentFrameNo
                    df = df.loc[:row_max]
                    levels = df.columns.levels
                    col = ['{0}{1}'.format(l,i) for i in levels[0] for l in levels[1]]
                    df.columns = col

                    df.to_csv(filePath)

            for k, v in self.data_dict.items():
                path = os.path.join(dirctory, '{0}-{1}.json'.format(base_name, k))
                filePath, _ = QFileDialog.getSaveFileName(None, 'Save JSON 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)

            path = os.path.join(dirctory, '{0}-colors.color'.format(base_name))
            filePath, _ = QFileDialog.getSaveFileName(None, 'Save Color File', path, "Color files (*.color)")
            if len(filePath) is not 0:
                logger.debug("Saving Color file: {0}".format(filePath))
                self.trackingPathGroup.saveColors(filePath)

            self.savedFlag = True

    def radiusSpinBoxValueChanged(self, i):
        if self.trackingPathGroup is not None:
            self.trackingPathGroup.setRadius(i)
        self.updateInputGraphicsView()

    def lineWidthSpinBoxValueChanged(self, i):
        if self.trackingPathGroup is not None:
            self.trackingPathGroup.setLineWidth(i)
        self.updateInputGraphicsView()

    def overlayFrameNoSpinBoxValueChanged(self, i):
        if self.trackingPathGroup is not None:
            self.trackingPathGroup.setOverlayFrameNo(i)
        self.updateInputGraphicsView()

    def stackedWidgetCurrentChanged(self, i):
        currentWidget = self.stackedWidget.currentWidget()

        currentWidget.estimator_init()
        self.algorithmSettingsGroupBox.setTitle(currentWidget.get_name())
        self.resetDataframe()

    def updateInputGraphicsView(self):
        if self.inputPixmapItem is not None:
            self.inputScene.removeItem(self.inputPixmapItem)

        if self.filter is not None and hasattr(self.filter, "resize_flag") and self.filter.resize_flag:
            qimg = misc.cvMatToQImage(cv2.pyrDown(self.cv_img))
        else:
            qimg = misc.cvMatToQImage(self.cv_img)

        pixmap = QPixmap.fromImage(qimg)

        self.inputPixmapItem = QGraphicsPixmapItem(pixmap)
        rect = QtCore.QRectF(pixmap.rect())
        self.inputScene.setSceneRect(rect)
        self.inputScene.addItem(self.inputPixmapItem)

        self.inputGraphicsView.viewport().update()
        self.inputGraphicsViewResized()

    def inputGraphicsViewResized(self, event=None):
        self.inputGraphicsView.fitInView(self.inputScene.sceneRect(), QtCore.Qt.KeepAspectRatio)

    def updatePath(self):
        try:
            attrs = self.stackedWidget.currentWidget().get_attributes()
            attrs.keys()
        except Exception as e:
            msg = 'Tracking Lib. Attributes Error:\n{}'.format(e)
            self.generateCriticalMessage(msg)
            return

        if 'position' in attrs:
            self.trackingPathGroup.setPoints(self.currentFrameNo)

        if 'arrow' in attrs:
            for i, arrow_item in enumerate(self.item_dict['arrow']):
                begin = self.df['position'].loc[self.currentFrameNo, i].as_matrix()
                end = self.df['arrow'].loc[self.currentFrameNo, i].as_matrix()
                arrow_item.setPosition(begin, end)

        if 'path' in attrs:
            for path_item, path_data in zip(self.item_dict['path'], self.data_dict['path'][self.currentFrameNo]):
                poly = QPolygonF()
                for p in path_data:
                    poly.append(QPointF(*p))

                painter_path = QPainterPath()
                painter_path.addPolygon(poly)
                path_item.setPath(painter_path)

                pen = QPen(Qt.blue)
                pen.setWidth(2)
                path_item.setPen(pen)

        if 'polygon' in attrs:
            for path_item, path_data in zip(self.item_dict['polygon'], self.data_dict['polygon'][self.currentFrameNo]):
                poly = QPolygonF()
                for p in path_data:
                    poly.append(QPointF(*p))

                painter_path = QPainterPath()
                painter_path.addPolygon(poly)
                path_item.setPath(painter_path)

                pen = QPen(Qt.black)
                pen.setWidth(1)
                path_item.setPen(pen)

        if 'rect' in attrs:
            for rect_item, rect in zip(self.item_dict['rect'], self.data_dict['rect'][self.currentFrameNo]):
                rect_item.setRect(QRectF(QPointF(*rect[0]), QPointF(*rect[1])))

    def resetDataframe(self):
        self.initializeTrackingSystem()
        self.evaluate()

    def restartDataframe(self):
        if len(self.df.keys())==0:
            return

        for attr in self.df.keys():
            self.df[attr].loc[self.currentFrameNo+1:] = np.nan
        for k in self.data_dict.keys():
            for kk in self.data_dict[k]:
                if kk>currentFrameNo:
                    del self.data_dict[k]

        df = {}
        for attr in self.df.keys():
            df[attr] = self.df[attr].loc[self.currentFrameNo]

        kv = {k:[] for k in self.df.keys()}
        for key, value in kv.items():
            mul_levs = df[key].index.levels
            for i in mul_levs[0]:
                value.append(df[key][i].as_matrix())

        for k, v in self.data_dict.items():
            kv[key] = v

        for key, value in kv.items():
            kv[key] = np.array(value)

        try:
            widget = self.stackedWidget.currentWidget()
            widget.reset_estimator(kv)
        except Exception as e:
            msg = 'Tracking Lib. Reset Fail:\n{}'.format(e)
            self.generateCriticalMessage(msg)

    def removeTrackingGraphicsItems(self):
        if self.trackingPathGroup is not None:
            self.inputScene.removeItem(self.trackingPathGroup)
            self.trackingPathGroup = None

        for k, v in self.item_dict.items():
            [self.inputScene.removeItem(item) for item in v]
            v.clear()

    def initializeTrackingSystem(self):
        self.isInitialized = False
        if  not (self.videoPlaybackWidget.isOpened() and filterOperation is not None):
            return False

        if self.currentFrameNo != 0:
            ret, frame = self.videoPlaybackWidget.readFrame(0)
            self.cv_img = frame
            self.currentFrameNo = 0
            self.videoPlaybackWidget.setSliderValueWithoutSignal(0)

        self.filter = filterOperation(self.cv_img)
        self.filter.fgbg = self.filterIO.getBackgroundImg()
        self.filter.isInit = True

        try:
            tracking_n = self.stackedWidget.currentWidget().get_tracking_n()
            attrs = self.stackedWidget.currentWidget().get_attributes()
            attrs.keys()
        except Exception as e:
            msg = 'Tracking Lib. Tracking N or attributes Error:\n{}'.format(e)
            self.generateCriticalMessage(msg)
            return

        max_frame_pos = self.videoPlaybackWidget.getMaxFramePos()

        self.df = {}
        for k, t in attrs.items():
            if t is None:
                self.data_dict[k] = {}
                self.data_dict[k]['name'] = k
            else:
                tuples = []
                for i in range(tracking_n):
                    for v in t:
                        tuples.append((i, v))
                col = pd.MultiIndex.from_tuples(tuples)
                self.df[k] = pd.DataFrame(index=range(max_frame_pos+1), columns=col, dtype=np.float64).sort_index().sort_index(axis=1)
                self.df[k].index.name = k

        self.removeTrackingGraphicsItems()

        if 'position' in attrs:
            self.trackingPathGroup = TrackingPathGroup()
            self.trackingPathGroup.setRect(self.inputScene.sceneRect())
            if self.pathCheckBox.checkState()==Qt.Unchecked:
                self.trackingPathGroup.hide()

            self.inputScene.addItem(self.trackingPathGroup)
            self.trackingPathGroup.setDataFrame(self.df['position'])

            lw = self.trackingPathGroup.autoAdjustLineWidth(self.cv_img.shape)
            r = self.trackingPathGroup.autoAdjustRadius(self.cv_img.shape)
            self.lineWidthSpinBox.setValue(lw)
            self.radiusSpinBox.setValue(r)

            self.trackingPathGroup.setItemsAreMovable(True)

        if 'rect' in attrs:
            self.item_dict['rect'] = [QGraphicsRectItem() for i in range(tracking_n)]
            for rect_item in self.item_dict['rect']:
                rect_item.setZValue(1000)
                self.inputScene.addItem(rect_item)

        if 'arrow' in attrs:
            self.item_dict['arrow'] = [MovableArrow() for i in range(tracking_n)]
            for arrow_item in self.item_dict['arrow']:
                arrow_item.setZValue(900)
                if self.arrowCheckBox.checkState()==Qt.Unchecked:
                    arrow_item.hide()
                self.inputScene.addItem(arrow_item)

        if 'path' in attrs:
            self.item_dict['path'] = [QGraphicsPathItem() for i in range(tracking_n)]
            for path_item in self.item_dict['path']:
                path_item.setZValue(900)
                self.inputScene.addItem(path_item)

        if 'polygon' in attrs:
            self.item_dict['polygon'] = [QGraphicsPathItem() for i in range(tracking_n)]
            for path_item in self.item_dict['polygon']:
                path_item.setZValue(900)
                self.inputScene.addItem(path_item)

        self.videoPlaybackWidget.setMaxTickableFrameNo(0)
        # if self.currentFrameNo != 0:
        #     self.videoPlaybackWidget.moveToFrame(0)
        self.videoPlaybackWidget.setPlaybackDelta(self.playbackDeltaSpinBox.value())

        self.isInitialized = True

    def evaluate(self, update=True):
        if not self.isInitialized:
            return

        if len(self.df.keys())!=0 and np.all([np.all(pd.notnull(df.loc[self.currentFrameNo])) for df in self.df.values()]):
            print('update')
            self.updatePath()
            self.updateInputGraphicsView()
            self.updateFrame.emit()
            return

        img = self.filter.filterFunc(self.cv_img.copy())

        try:
            widget = self.stackedWidget.currentWidget()
            res = widget.track(self.cv_img.copy(), img)
            attrs = widget.get_attributes()
        except Exception as e:
            msg = 'Tracking Lib. Tracking method Fail:\n{}'.format(e)
            self.generateCriticalMessage(msg)
            return

        for k,v in res.items():
            if k=='path' or k=='rect' or k=='polygon':
                self.data_dict[k][self.currentFrameNo] = ndarray_to_list(v)
                continue
            if not attrs[k]:
                continue
            for i in range(len(v)):
                self.df[k].loc[self.currentFrameNo, i] = v[i]

        self.videoPlaybackWidget.setMaxTickableFrameNo(self.currentFrameNo+self.videoPlaybackWidget.playbackDelta)
        self.savedFlag = False

        if update:
            self.updatePath()
            self.updateInputGraphicsView()
            self.updateFrame.emit()

    def runObjectTracking(self):
        if self.filter is None or not self.videoPlaybackWidget.isOpened():
            return
        minFrame = self.currentFrameNo
        maxFrame = self.videoPlaybackWidget.getMaxFramePos()
        numFrames = maxFrame-minFrame
        progress = QProgressDialog("Running...", "Abort", 0, numFrames, self)

        progress.setWindowModality(Qt.WindowModal)

        currentFrameNo = self.currentFrameNo
        for i, frameNo in enumerate(range(minFrame, maxFrame+1)):
            progress.setValue(i)
            if progress.wasCanceled():
                break

            ret, frame = self.videoPlaybackWidget.readFrame(frameNo)
            self.cv_img = frame
            self.currentFrameNo = frameNo
            self.evaluate(False)

        self.videoPlaybackWidget.moveToFrame(currentFrameNo)
        progress.setValue(numFrames)

    def eventFilter(self, obj, event):
        if event.type() == QEvent.KeyPress:
            print(event.key())
            if Qt.Key_Home <= event.key() <= Qt.Key_PageDown:
                self.videoPlaybackWidget.playbackSlider.keyPressEvent(event)
                return True

        return False

    def generateCriticalMessage(self, msg):
        tb = sys.exc_info()[-1]
        f = tb.tb_frame
        msg = 'File name: {0}\nLine No: {1}\n'.format(f.f_code.co_filename, tb.tb_lineno) + msg
        reply = QtWidgets.QMessageBox.critical(
                self,
                'Critical',
                msg,
                QtWidgets.QMessageBox.Ok,
                QtWidgets.QMessageBox.NoButton
                )
        return reply
Exemple #11
0
    def initializeTrackingSystem(self):
        self.isInitialized = False
        self.removeTrackingGraphicsItems()

        if hasattr(self, 'currentFrameNo') and self.currentFrameNo != 0:
            ret, frame = self.videoPlaybackWidget.readFrame(0)
            self.cv_img = frame
            self.updateInputGraphicsView()
            self.currentFrameNo = 0
            self.videoPlaybackWidget.setSliderValueWithoutSignal(0)

        self.videoPlaybackWidget.setMaxTickableFrameNo(0)

        try:
            tracking_n = self.stackedWidget.currentWidget().get_tracking_n()
            attrs = self.stackedWidget.currentWidget().get_attributes()
            is_filter_required = self.stackedWidget.currentWidget().is_filter_required()
            attrs.keys()
        except Exception as e:
            msg = 'Tracking Lib. Tracking N or attributes Error:\n{}'.format(e)
            self.generateCriticalMessage(msg)
            return

        if not (self.videoPlaybackWidget.isOpened() and
           (filterOperation is not None or not is_filter_required)):
            return False


        if is_filter_required:
            self.filter = filterOperation(self.cv_img)
            self.filter.fgbg = self.filterIO.getBackgroundImg()
            self.filter.isInit = True
        else:
            self.filter = None

        max_frame_pos = self.videoPlaybackWidget.getMaxFramePos()

        self.df = {}
        for k, t in attrs.items():
            if t is None:
                self.data_dict[k] = {}
                self.data_dict[k]['name'] = k
            else:
                tuples = []
                for i in range(tracking_n):
                    for v in t:
                        tuples.append((i, v))
                col = pd.MultiIndex.from_tuples(tuples)
                self.df[k] = pd.DataFrame(
                    index=range(0, max_frame_pos+1, self.playbackDeltaSpinBox.value()),
                    columns=col,
                    dtype=np.float64
                    ).sort_index().sort_index(axis=1)
                self.df[k].index.name = k

        if 'position' in attrs:
            self.trackingPathGroup = TrackingPathGroup()
            self.trackingPathGroup.setRect(self.inputScene.sceneRect())
            if self.pathCheckBox.checkState()==Qt.Unchecked:
                self.trackingPathGroup.hide()

            self.inputScene.addItem(self.trackingPathGroup)
            self.trackingPathGroup.setDataFrame(self.df['position'])

            lw = self.trackingPathGroup.autoAdjustLineWidth(self.cv_img.shape)
            r = self.trackingPathGroup.autoAdjustRadius(self.cv_img.shape)
            self.trackingPathGroup.setOverlayFrameNo(self.overlayFrameNoSpinBox.value())
            self.lineWidthSpinBox.setValue(lw)
            self.radiusSpinBox.setValue(r)

            self.trackingPathGroup.setItemsAreMovable(True)

        if 'rect' in attrs:
            self.item_dict['rect'] = [QGraphicsRectItem() for i in range(tracking_n)]
            for rect_item in self.item_dict['rect']:
                rect_item.setZValue(1000)
                self.inputScene.addItem(rect_item)

        if 'arrow' in attrs:
            self.item_dict['arrow'] = [MovableArrow() for i in range(tracking_n)]
            for arrow_item in self.item_dict['arrow']:
                arrow_item.setZValue(900)
                if self.arrowCheckBox.checkState()==Qt.Unchecked:
                    arrow_item.hide()
                self.inputScene.addItem(arrow_item)

        if 'path' in attrs:
            self.item_dict['path'] = [QGraphicsPathItem() for i in range(tracking_n)]
            for path_item in self.item_dict['path']:
                path_item.setZValue(900)
                self.inputScene.addItem(path_item)

        if 'polygon' in attrs:
            self.item_dict['polygon'] = [QGraphicsPathItem() for i in range(tracking_n)]
            for path_item in self.item_dict['polygon']:
                path_item.setZValue(900)
                self.inputScene.addItem(path_item)

        # if self.currentFrameNo != 0:
        #     self.videoPlaybackWidget.moveToFrame(0)
        self.videoPlaybackWidget.setPlaybackDelta(self.playbackDeltaSpinBox.value())

        self.isInitialized = True
Exemple #12
0
class Ui_MainWindow(QMainWindow, Ui_MainWindowBase):
    updateFrame = pyqtSignal()

    def __init__(self):
        super(Ui_MainWindow, self).__init__()
        self.setupUi(self)

        self.videoPlaybackInit()
        self.imgInit()
        self.menuInit()

        self.radiusSpinBox.valueChanged.connect(self.radiusSpinBoxValueChanged)
        self.lineWidthSpinBox.valueChanged.connect(self.lineWidthSpinBoxValueChanged)
        self.overlayFrameNoSpinBox.valueChanged.connect(self.overlayFrameNoSpinBoxValueChanged)
        self.stackedWidget.currentChanged.connect(self.stackedWidgetCurrentChanged)

        self.arrowCheckBox.stateChanged.connect(self.arrowCheckBoxStateChanged)
        self.pathCheckBox.stateChanged.connect(self.pathCheckBoxStateChanged)
        self.reverseArrowColorCheckBox.stateChanged.connect(self.reverseArrowColorCheckBoxStateChanged)

        self.opaqueCheckBox.stateChanged.connect(self.opaqueCheckBoxStateChanged)

        self.videoPlaybackWidget.setSignalSlotMode()
        self.updateFrame.connect(self.videoPlaybackWidget.videoPlayback)

        self.filter = None
        self.filterIO = None
        self.isInitialized = False
        self.item_dict = {}
        self.data_dict = {}
        self.trackingPathGroup = None
        self.df = {}
        self.inputPixmapItem = None
        self.cv_img = None
        self.filePath = None
        self.savedFlag = True

        QShortcut(QKeySequence("Ctrl+R"), self, self.restartDataframe)
        QShortcut(QKeySequence("Ctrl+S"), self, self.saveCSVFile)

    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 len(self.df.keys())==0 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:
            event.accept()
        else:
            event.ignore()

    def processDropedFile(self,filePath):
        root,ext = os.path.splitext(filePath)
        if ext == ".filter":
            # Read Filter
            self.openFilterFile(filePath=filePath)
            return
        elif self.openVideoFile(filePath=filePath):
            return

    def arrowCheckBoxStateChanged(self, state):
        if 'arrow' not in self.item_dict.keys():
            return

        for arrow_item in self.item_dict['arrow']:
            if state==Qt.Unchecked:
                arrow_item.hide()
            if state==Qt.Checked:
                arrow_item.show()

        self.updateInputGraphicsView()

    def pathCheckBoxStateChanged(self, state):
        if self.trackingPathGroup is None:
            return

        if state==Qt.Unchecked:
            self.trackingPathGroup.hide()
        if state==Qt.Checked:
            self.trackingPathGroup.show()

        self.updateInputGraphicsView()

    def reverseArrowColorCheckBoxStateChanged(self, state):
        if 'arrow' not in self.item_dict.keys():
            return

        for arrow_item in self.item_dict['arrow']:
            if state==Qt.Unchecked:
                arrow_item.setColor([0,0,0])
            if state==Qt.Checked:
                arrow_item.setColor([255,255,255])

        self.updateInputGraphicsView()

    def opaqueCheckBoxStateChanged(self, state):
        if self.trackingPathGroup is None:
            return

        if state==Qt.Unchecked:
            self.trackingPathGroup.setOpacity(0.5)
        if state==Qt.Checked:
            self.trackingPathGroup.setOpacity(1.0)

        self.updateInputGraphicsView()

    def reset(self):
        self.videoPlaybackWidget.stop()
        self.videoPlaybackWidget.moveToFrame(0)

    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()
        return

    def imgInit(self):
        self.inputScene = QGraphicsScene()
        self.inputGraphicsView.setScene(self.inputScene)
        self.inputGraphicsView.resizeEvent = self.inputGraphicsViewResized

    def menuInit(self):
        self.actionOpenVideo.triggered.connect(self.openVideoFile)
        self.actionOpenFilterSetting.triggered.connect(self.openFilterFile)

        self.actionSaveCSVFile.triggered.connect(self.saveCSVFile)

        self.actionRunObjectTracking.triggered.connect(self.runObjectTracking)
        self.actionTrackingPathColor.triggered.connect(self.openTrackingPathColorSelectorDialog)

        self.menuAlgorithmsActionGroup = QActionGroup(self.menuAlgorithms)

        path_list = [[tracking_system_path, currentDirPath], ]
        if os.path.exists(user_defined_lib_path):
            path_list.append([user_defined_tracking_system_path, user_defined_lib_path])
        for system_path in path_list:
            for module_path in get_modules(system_path[0], system_path[1]):
                module_str = '.'.join(module_path)

                try:
                    module = importlib.import_module(module_str)

                    if not hasattr(module, 'Widget'):
                        continue

                    class_def = getattr(module, "Widget")
                    if not issubclass(class_def, QtWidgets.QWidget):
                        continue

                    widget = class_def(self.stackedWidget)
                    widget.reset.connect(self.resetDataframe)
                    widget.restart.connect(self.restartDataframe)
                    self.stackedWidget.addWidget(widget)

                    action = self.menuAlgorithms.addAction(widget.get_name())
                    action.triggered.connect(self.generateAlgorithmsMenuClicked(widget))
                    action.setCheckable(True)
                    action.setActionGroup(self.menuAlgorithmsActionGroup)

                    if len(self.menuAlgorithmsActionGroup.actions()) == 1:
                        action.setChecked(True)
                        self.algorithmSettingsGroupBox.setTitle(widget.get_name())

                except Exception as e:
                    if system_path[1] is user_defined_lib_path:
                        msg = 'Tracking Lib. Load Fail: {0}\n{1}'.format(module_str, e)
                        self.generateCriticalMessage(msg)
                    continue

    def openTrackingPathColorSelectorDialog(self, activated=False):
        if self.trackingPathGroup is not None:
            self.trackingPathGroup.openColorSelectorDialog(self)

    def generateAlgorithmsMenuClicked(self, widget):
        def action_triggered(activated=False):
            if widget is not self.stackedWidget.currentWidget():
                self.stackedWidget.setCurrentWidget(widget)
            else:
                pass
        return action_triggered

    def initializeEventDialog(self):
        quit_msg = "Data is not saved.\nAre you sure you want to reset?"
        reply = QtWidgets.QMessageBox.question(
                self,
                'Warning',
                quit_msg,
                QtWidgets.QMessageBox.Yes,
                QtWidgets.QMessageBox.No
                )

        if reply == QtWidgets.QMessageBox.Yes:
            return True
        else:
            return False

    def openVideoFile(self, activated=False, filePath = None):
        if filePath is None:
            filePath, _ = QFileDialog.getOpenFileName(None, 'Open Video File', userDir)

        global filterOperation
        if len(filePath) is not 0:
            if filterOperation is not None and self.videoPlaybackWidget.isOpened():
                if self.initializeEventDialog():
                    filterOperation = None
                    self.removeTrackingGraphicsItems()
                    self.savedFlag = True
                else:
                    return
            self.filePath = filePath
            ret = self.videoPlaybackWidget.openVideo(filePath)
            if not ret:
                return False

            self.videoPlaybackWidget.show()

            self.cv_img = self.videoPlaybackWidget.getCurrentFrame()
            self.currentFrameNo = 0
            self.videoPlaybackWidget.setMaxTickableFrameNo(0)
            self.initializeTrackingSystem()

            return True
        else:
            return False

    def openFilterFile(self, activated=False, filePath = None):
        if filePath is None:
            filePath, _ = QFileDialog.getOpenFileName(None, 'Open Block File', userDir, "Block files (*.filter)")

        if len(filePath) is not 0:
            if filterOperation is not None and self.videoPlaybackWidget.isOpened():
                if self.initializeEventDialog():
                    self.videoPlaybackWidget.closeVideo()
                    self.videoPlaybackWidget.hide()
                    self.removeTrackingGraphicsItems()
                    self.inputScene.removeItem(self.inputPixmapItem)
                    self.savedFlag = True
                else:
                    return
            logger.debug("Open Filter file: {0}".format(filePath))

            self.filterIO = FilterIO(filePath)

            exec(self.filterIO.getFilterCode(), globals())

            self.filter = None

            self.initializeTrackingSystem()
            self.evaluate()

    def saveCSVFile(self, activated=False, filePath = None):
        if len(self.df.keys())!=0:
            dirctory = os.path.dirname(self.filePath)
            base_name = os.path.splitext(os.path.basename(self.filePath))[0]

            path = os.path.join(dirctory, '{0}-{1}.txt'.format(base_name, "info"))
            filePath, _ = QFileDialog.getSaveFileName(None, 'Save Info File', path, "TXT files (*.txt)")

            if len(filePath) is not 0:
                logger.debug("Saving Info file: {0}".format(filePath))

                with open(filePath, 'w') as fp:
                    fp.write(self.videoPlaybackWidget.getVideoInfo())

            for attr, df in self.df.items():
                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))

                    df = df.copy().dropna()
                    levels = df.columns.levels
                    col = ['{0}{1}'.format(l,i) for i in levels[0] for l in levels[1]]
                    df.columns = col

                    df.to_csv(filePath)

            for k, v in self.data_dict.items():
                path = os.path.join(dirctory, '{0}-{1}.json'.format(base_name, k))
                filePath, _ = QFileDialog.getSaveFileName(None, 'Save JSON 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)

            path = os.path.join(dirctory, '{0}-colors.color'.format(base_name))
            filePath, _ = QFileDialog.getSaveFileName(None, 'Save Color File', path, "Color files (*.color)")
            if len(filePath) is not 0:
                logger.debug("Saving Color file: {0}".format(filePath))
                self.trackingPathGroup.saveColors(filePath)

            self.savedFlag = True

    def radiusSpinBoxValueChanged(self, i):
        if self.trackingPathGroup is not None:
            self.trackingPathGroup.setRadius(i)
        self.updateInputGraphicsView()

    def lineWidthSpinBoxValueChanged(self, i):
        if self.trackingPathGroup is not None:
            self.trackingPathGroup.setLineWidth(i)
        self.updateInputGraphicsView()

    def overlayFrameNoSpinBoxValueChanged(self, i):
        if self.trackingPathGroup is not None:
            self.trackingPathGroup.setOverlayFrameNo(i)
        self.updateInputGraphicsView()

    def stackedWidgetCurrentChanged(self, i):
        currentWidget = self.stackedWidget.currentWidget()

        currentWidget.estimator_init()
        self.algorithmSettingsGroupBox.setTitle(currentWidget.get_name())
        self.resetDataframe()

    def updateInputGraphicsView(self):
        if self.inputPixmapItem is not None:
            self.inputScene.removeItem(self.inputPixmapItem)

        if self.filter is not None and hasattr(self.filter, "resize_flag") and self.filter.resize_flag:
            qimg = misc.cvMatToQImage(cv2.pyrDown(self.cv_img))
        else:
            qimg = misc.cvMatToQImage(self.cv_img)

        pixmap = QPixmap.fromImage(qimg)

        self.inputPixmapItem = QGraphicsPixmapItem(pixmap)
        rect = QtCore.QRectF(pixmap.rect())
        self.inputScene.setSceneRect(rect)
        self.inputScene.addItem(self.inputPixmapItem)

        self.inputGraphicsView.viewport().update()
        self.inputGraphicsViewResized()

    def inputGraphicsViewResized(self, event=None):
        self.inputGraphicsView.fitInView(self.inputScene.sceneRect(), QtCore.Qt.KeepAspectRatio)

    def updatePath(self):
        try:
            attrs = self.stackedWidget.currentWidget().get_attributes()
            attrs.keys()
        except Exception as e:
            msg = 'Tracking Lib. Attributes Error:\n{}'.format(e)
            self.generateCriticalMessage(msg)
            return

        if 'position' in attrs:
            self.trackingPathGroup.setPoints(self.currentFrameNo)

        if 'arrow' in attrs:
            for i, arrow_item in enumerate(self.item_dict['arrow']):
                begin = self.df['position'].loc[self.currentFrameNo, i].values
                end = self.df['arrow'].loc[self.currentFrameNo, i].values
                arrow_item.setPosition(begin, end)

        if 'path' in attrs:
            for path_item, path_data in zip(self.item_dict['path'], self.data_dict['path'][self.currentFrameNo]):
                poly = QPolygonF()
                for p in path_data:
                    poly.append(QPointF(*p))

                painter_path = QPainterPath()
                painter_path.addPolygon(poly)
                path_item.setPath(painter_path)

                pen = QPen(Qt.blue)
                pen.setWidth(2)
                path_item.setPen(pen)

        if 'polygon' in attrs:
            for path_item, path_data in zip(self.item_dict['polygon'], self.data_dict['polygon'][self.currentFrameNo]):
                poly = QPolygonF()
                for p in path_data:
                    poly.append(QPointF(*p))

                painter_path = QPainterPath()
                painter_path.addPolygon(poly)
                path_item.setPath(painter_path)

                pen = QPen(Qt.black)
                pen.setWidth(1)
                path_item.setPen(pen)

        if 'rect' in attrs:
            for rect_item, rect in zip(self.item_dict['rect'], self.data_dict['rect'][self.currentFrameNo]):
                rect_item.setRect(QRectF(QPointF(*rect[0]), QPointF(*rect[1])))

    def resetDataframe(self):
        self.initializeTrackingSystem()
        self.evaluate()

    def restartDataframe(self):
        if len(self.df.keys()) == 0:
            return

        for attr in self.df.keys():
            self.df[attr].loc[self.currentFrameNo+1:] = np.nan

        for k in list(self.data_dict.keys()):
            for kk in list(self.data_dict[k].keys()):
                if kk == 'name':
                    continue
                elif int(kk) > self.currentFrameNo:
                    del self.data_dict[k][kk]

        df = {}
        for attr in self.df.keys():
            df[attr] = self.df[attr].loc[self.currentFrameNo]

        kv = {k: [] for k in self.df.keys()}
        for key, value in kv.items():
            mul_levs = df[key].index.levels
            for i in mul_levs[0]:
                value.append(df[key][i].values)

            kv[key] = np.array(value)

        for key, value in self.data_dict.items():
            kv[key] = [np.array(v) for v in value[self.currentFrameNo]]

        self.videoPlaybackWidget.setMaxTickableFrameNo(
            self.currentFrameNo + self.videoPlaybackWidget.playbackDelta
        )

        try:
            widget = self.stackedWidget.currentWidget()
            widget.reset_estimator(kv)
        except Exception as e:
            msg = 'Tracking Lib. Reset Fail:\n{}'.format(e)
            self.generateCriticalMessage(msg)

    def removeTrackingGraphicsItems(self):
        if self.trackingPathGroup is not None:
            self.inputScene.removeItem(self.trackingPathGroup)
            self.trackingPathGroup = None

        for k, v in self.item_dict.items():
            [self.inputScene.removeItem(item) for item in v]
            v.clear()

    def initializeTrackingSystem(self):
        self.isInitialized = False
        self.removeTrackingGraphicsItems()

        if hasattr(self, 'currentFrameNo') and self.currentFrameNo != 0:
            ret, frame = self.videoPlaybackWidget.readFrame(0)
            self.cv_img = frame
            self.updateInputGraphicsView()
            self.currentFrameNo = 0
            self.videoPlaybackWidget.setSliderValueWithoutSignal(0)

        self.videoPlaybackWidget.setMaxTickableFrameNo(0)

        try:
            tracking_n = self.stackedWidget.currentWidget().get_tracking_n()
            attrs = self.stackedWidget.currentWidget().get_attributes()
            is_filter_required = self.stackedWidget.currentWidget().is_filter_required()
            attrs.keys()
        except Exception as e:
            msg = 'Tracking Lib. Tracking N or attributes Error:\n{}'.format(e)
            self.generateCriticalMessage(msg)
            return

        if not (self.videoPlaybackWidget.isOpened() and
           (filterOperation is not None or not is_filter_required)):
            return False


        if is_filter_required:
            self.filter = filterOperation(self.cv_img)
            self.filter.fgbg = self.filterIO.getBackgroundImg()
            self.filter.isInit = True
        else:
            self.filter = None

        max_frame_pos = self.videoPlaybackWidget.getMaxFramePos()

        self.df = {}
        for k, t in attrs.items():
            if t is None:
                self.data_dict[k] = {}
                self.data_dict[k]['name'] = k
            else:
                tuples = []
                for i in range(tracking_n):
                    for v in t:
                        tuples.append((i, v))
                col = pd.MultiIndex.from_tuples(tuples)
                self.df[k] = pd.DataFrame(
                    index=range(0, max_frame_pos+1, self.playbackDeltaSpinBox.value()),
                    columns=col,
                    dtype=np.float64
                    ).sort_index().sort_index(axis=1)
                self.df[k].index.name = k

        if 'position' in attrs:
            self.trackingPathGroup = TrackingPathGroup()
            self.trackingPathGroup.setRect(self.inputScene.sceneRect())
            if self.pathCheckBox.checkState()==Qt.Unchecked:
                self.trackingPathGroup.hide()

            self.inputScene.addItem(self.trackingPathGroup)
            self.trackingPathGroup.setDataFrame(self.df['position'])

            lw = self.trackingPathGroup.autoAdjustLineWidth(self.cv_img.shape)
            r = self.trackingPathGroup.autoAdjustRadius(self.cv_img.shape)
            self.trackingPathGroup.setOverlayFrameNo(self.overlayFrameNoSpinBox.value())
            self.lineWidthSpinBox.setValue(lw)
            self.radiusSpinBox.setValue(r)

            self.trackingPathGroup.setItemsAreMovable(True)

        if 'rect' in attrs:
            self.item_dict['rect'] = [QGraphicsRectItem() for i in range(tracking_n)]
            for rect_item in self.item_dict['rect']:
                rect_item.setZValue(1000)
                self.inputScene.addItem(rect_item)

        if 'arrow' in attrs:
            self.item_dict['arrow'] = [MovableArrow() for i in range(tracking_n)]
            for arrow_item in self.item_dict['arrow']:
                arrow_item.setZValue(900)
                if self.arrowCheckBox.checkState()==Qt.Unchecked:
                    arrow_item.hide()
                self.inputScene.addItem(arrow_item)

        if 'path' in attrs:
            self.item_dict['path'] = [QGraphicsPathItem() for i in range(tracking_n)]
            for path_item in self.item_dict['path']:
                path_item.setZValue(900)
                self.inputScene.addItem(path_item)

        if 'polygon' in attrs:
            self.item_dict['polygon'] = [QGraphicsPathItem() for i in range(tracking_n)]
            for path_item in self.item_dict['polygon']:
                path_item.setZValue(900)
                self.inputScene.addItem(path_item)

        # if self.currentFrameNo != 0:
        #     self.videoPlaybackWidget.moveToFrame(0)
        self.videoPlaybackWidget.setPlaybackDelta(self.playbackDeltaSpinBox.value())

        self.isInitialized = True

    def evaluate(self, update=True):
        if not self.isInitialized:
            return

        if self.currentFrameNo + 1 < self.videoPlaybackWidget.getMaxTickableFrameNo():
            print('update')
            self.updatePath()
            self.updateInputGraphicsView()
            self.updateFrame.emit()
            return

        if self.filter is not None:
            img = self.filter.filterFunc(self.cv_img.copy())
        else:
            img = None

        try:
            widget = self.stackedWidget.currentWidget()
            prev_pos = self.videoPlaybackWidget.getPrevFramePos()
            attrs = widget.get_attributes()

            if prev_pos >= 0:
                prev_data = {
                    k: self.data_dict[k][prev_pos]
                    for k in self.data_dict.keys()
                }

                for k in self.df.keys():
                    df = self.df[k]
                    prev_data[k] = [
                        np.copy(df.loc[prev_pos, i].values)
                        for i in df.columns.levels[0]
                    ]
            else:
                prev_data = {k: None for k in attrs.keys()}

            prev_data['ignore_error'] = (
                self.ignoreMisDetectionErrorCheckBox.checkState() == Qt.Checked
            )

            res = widget.track(
                self.cv_img.copy(),
                img,
                prev_data
            )
        except Exception as e:
            self.videoPlaybackWidget.stop()
            self.videoPlaybackWidget.moveToFrame(
                max(0, self.currentFrameNo - self.videoPlaybackWidget.playbackDelta)
            )
            msg = 'Tracking Lib. Tracking method Fail:\n{}'.format(e)
            self.generateCriticalMessage(msg)
            return

        for k, v in res.items():
            if k == 'path' or k == 'rect' or k == 'polygon':
                self.data_dict[k][self.currentFrameNo] = ndarray_to_list(v)
                continue
            if not attrs[k]:
                continue
            for i in range(len(v)):
                self.df[k].loc[self.currentFrameNo, i] = v[i]

        maxTickableFrameNo = \
            self.currentFrameNo + self.videoPlaybackWidget.playbackDelta
        if maxTickableFrameNo > self.videoPlaybackWidget.getMaxFramePos():
            maxTickableFrameNo = self.currentFrameNo

        self.videoPlaybackWidget.setMaxTickableFrameNo(maxTickableFrameNo)
        self.savedFlag = False

        if update:
            self.updatePath()
            self.updateInputGraphicsView()
            self.updateFrame.emit()

    def runObjectTracking(self):
        if self.filter is None or not self.videoPlaybackWidget.isOpened():
            return
        minFrame = self.currentFrameNo
        maxFrame = self.videoPlaybackWidget.getMaxFramePos()
        numFrames = maxFrame-minFrame
        progress = QProgressDialog("Running...", "Abort", 0, numFrames, self)

        progress.setWindowModality(Qt.WindowModal)

        currentFrameNo = self.currentFrameNo
        for i, frameNo in enumerate(range(minFrame, maxFrame+1)):
            progress.setValue(i)
            if progress.wasCanceled():
                break

            ret, frame = self.videoPlaybackWidget.readFrame(frameNo)
            self.cv_img = frame
            self.currentFrameNo = frameNo
            self.evaluate(False)

        self.videoPlaybackWidget.moveToFrame(currentFrameNo)
        progress.setValue(numFrames)

    def eventFilter(self, obj, event):
        if event.type() == QEvent.Wheel:
            self.videoPlaybackWidget.wheelEvent(event)
            return True

        if event.type() == QEvent.KeyPress:
            qwop = [Qt.Key_Q, Qt.Key_W, Qt.Key_O, Qt.Key_P]
            is_qwop = (True in map(lambda x: x == event.key(), qwop))

            is_arrow = (Qt.Key_Home <= event.key() <= Qt.Key_PageDown)

            if is_arrow or is_qwop:
                self.videoPlaybackWidget.keyPressEvent(event)
                return True

        return False

    def generateCriticalMessage(self, msg):
        tb = sys.exc_info()[-1]
        f = tb.tb_frame
        msg = 'File name: {0}\nLine No: {1}\n'.format(f.f_code.co_filename, tb.tb_lineno) + msg
        reply = QtWidgets.QMessageBox.critical(
                self,
                'Critical',
                msg,
                QtWidgets.QMessageBox.Ok,
                QtWidgets.QMessageBox.NoButton
                )
        return reply
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
Exemple #14
0
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)

                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