Example #1
0
    def __init__(self, *args, **kwargs):
        clickpoints.Addon.__init__(self, *args, **kwargs)
        # set the title and layout
        self.setWindowTitle("Excel Exporter - ClickPoints")
        layout = QtWidgets.QVBoxLayout(self)

        # add a file chooser for the output
        self.line_edit_file = AddQSaveFileChoose(layout, "Path", value=self.db._database_filename.replace('.cdb', '.xls'), file_type="Excel Workbook (*.xls)")
        # add a mode selector, which formatting should be used for the output
        self.combo_style = AddQComboBox(layout, "Mode", values=["Marker Count", "Marker Positions", "Track Positions"])
        self.button_run = QtWidgets.QPushButton("Export")
        self.button_run.clicked.connect(self.run)
        layout.addWidget(self.button_run)
Example #2
0
    def __init__(self, *args, **kwargs):
        clickpoints.Addon.__init__(self, *args, **kwargs)
        # set the title and layout
        self.setWindowTitle("Kymograph - ClickPoints")
        self.layout = QtWidgets.QVBoxLayout(self)

        # add some options
        # the frame number for the kymograph
        self.addOption(key="frames",
                       display_name="Frames",
                       default=50,
                       value_type="int",
                       tooltip="How many images to use for the kymograph.")
        self.input_count = AddQSpinBox(self.layout,
                                       "Frames:",
                                       value=self.getOption("frames"),
                                       float=False)
        self.linkOption("frames", self.input_count)

        # the with in pixel of each line
        self.addOption(key="width",
                       display_name="Width",
                       default=1,
                       value_type="int",
                       tooltip="The width of the slice to cut from the image.")
        self.input_width = AddQSpinBox(self.layout,
                                       "Width:",
                                       value=self.getOption("width"),
                                       float=False)
        self.linkOption("width", self.input_width)

        # the length scaling
        self.addOption(key="scaleLength",
                       display_name="Scale Length",
                       default=1,
                       value_type="float",
                       tooltip="What is distance a pixel represents.")
        self.input_scale1 = AddQSpinBox(self.layout,
                                        "Scale Length:",
                                        value=self.getOption("scaleLength"),
                                        float=True)
        self.linkOption("scaleLength", self.input_scale1)

        # the time scaling
        self.addOption(
            key="scaleTime",
            display_name="Scale Time",
            default=1,
            value_type="float",
            tooltip="What is the time difference between two images.")
        self.input_scale2 = AddQSpinBox(self.layout,
                                        "Scale Time:",
                                        value=self.getOption("scaleTime"),
                                        float=True)
        self.linkOption("scaleTime", self.input_scale2)

        # the colormap
        self.addOption(key="colormap",
                       display_name="Colormap",
                       default="None",
                       value_type="string",
                       tooltip="The colormap to use for the kymograph.")
        maps = ["None"]
        maps.extend(plt.colormaps())
        self.input_colormap = AddQComboBox(
            self.layout,
            "Colormap:",
            selectedValue=self.getOption("colormap"),
            values=maps)
        self.input_colormap.setEditable(True)
        self.linkOption("colormap", self.input_colormap)

        # the table listing the line objects
        self.tableWidget = QtWidgets.QTableWidget(0, 1, self)
        self.layout.addWidget(self.tableWidget)
        self.row_headers = ["Line Length"]
        self.tableWidget.setHorizontalHeaderLabels(self.row_headers)
        self.tableWidget.setMinimumHeight(180)
        self.setMinimumWidth(500)
        self.tableWidget.setCurrentCell(0, 0)
        self.tableWidget.cellClicked.connect(self.cellSelected)

        # add kymograph types
        self.my_type = self.db.setMarkerType("kymograph",
                                             "#ef7fff",
                                             self.db.TYPE_Line,
                                             text="#$marker_id")
        self.my_type2 = self.db.setMarkerType("kymograph_end", "#df00ff",
                                              self.db.TYPE_Normal)
        self.cp.reloadTypes()

        # add a plot widget
        self.plot = MatplotlibWidget(self)
        self.layout.addWidget(self.plot)
        self.layout.addWidget(NavigationToolbar(self.plot, self))
        self.plot.figure.canvas.mpl_connect('button_press_event',
                                            self.button_press_callback)

        # add export buttons
        layout = QtWidgets.QHBoxLayout()
        self.button_export = QtWidgets.QPushButton("Export")
        self.button_export.clicked.connect(self.export)
        layout.addWidget(self.button_export)
        self.button_export2 = QtWidgets.QPushButton("Export All")
        self.button_export2.clicked.connect(self.export2)
        layout.addWidget(self.button_export2)
        self.layout.addLayout(layout)

        # add a progress bar
        self.progressbar = QtWidgets.QProgressBar()
        self.layout.addWidget(self.progressbar)

        # connect slots
        self.signal_update_plot.connect(self.updatePlotImageEvent)
        self.signal_plot_finished.connect(self.plotFinishedEvent)

        # initialize the table
        self.updateTable()
        self.selected = None
Example #3
0
class Addon(clickpoints.Addon):
    signal_update_plot = QtCore.Signal()
    signal_plot_finished = QtCore.Signal()
    image_plot = None
    last_update = 0
    updating = False
    exporting = False
    exporting_index = 0

    def __init__(self, *args, **kwargs):
        clickpoints.Addon.__init__(self, *args, **kwargs)
        # set the title and layout
        self.setWindowTitle("Kymograph - ClickPoints")
        self.layout = QtWidgets.QVBoxLayout(self)

        # add some options
        # the frame number for the kymograph
        self.addOption(key="frames",
                       display_name="Frames",
                       default=50,
                       value_type="int",
                       tooltip="How many images to use for the kymograph.")
        self.input_count = AddQSpinBox(self.layout,
                                       "Frames:",
                                       value=self.getOption("frames"),
                                       float=False)
        self.linkOption("frames", self.input_count)

        # the with in pixel of each line
        self.addOption(key="width",
                       display_name="Width",
                       default=1,
                       value_type="int",
                       tooltip="The width of the slice to cut from the image.")
        self.input_width = AddQSpinBox(self.layout,
                                       "Width:",
                                       value=self.getOption("width"),
                                       float=False)
        self.linkOption("width", self.input_width)

        # the length scaling
        self.addOption(key="scaleLength",
                       display_name="Scale Length",
                       default=1,
                       value_type="float",
                       tooltip="What is distance a pixel represents.")
        self.input_scale1 = AddQSpinBox(self.layout,
                                        "Scale Length:",
                                        value=self.getOption("scaleLength"),
                                        float=True)
        self.linkOption("scaleLength", self.input_scale1)

        # the time scaling
        self.addOption(
            key="scaleTime",
            display_name="Scale Time",
            default=1,
            value_type="float",
            tooltip="What is the time difference between two images.")
        self.input_scale2 = AddQSpinBox(self.layout,
                                        "Scale Time:",
                                        value=self.getOption("scaleTime"),
                                        float=True)
        self.linkOption("scaleTime", self.input_scale2)

        # the colormap
        self.addOption(key="colormap",
                       display_name="Colormap",
                       default="None",
                       value_type="string",
                       tooltip="The colormap to use for the kymograph.")
        maps = ["None"]
        maps.extend(plt.colormaps())
        self.input_colormap = AddQComboBox(
            self.layout,
            "Colormap:",
            selectedValue=self.getOption("colormap"),
            values=maps)
        self.input_colormap.setEditable(True)
        self.linkOption("colormap", self.input_colormap)

        # the table listing the line objects
        self.tableWidget = QtWidgets.QTableWidget(0, 1, self)
        self.layout.addWidget(self.tableWidget)
        self.row_headers = ["Line Length"]
        self.tableWidget.setHorizontalHeaderLabels(self.row_headers)
        self.tableWidget.setMinimumHeight(180)
        self.setMinimumWidth(500)
        self.tableWidget.setCurrentCell(0, 0)
        self.tableWidget.cellClicked.connect(self.cellSelected)

        # add kymograph types
        self.my_type = self.db.setMarkerType("kymograph",
                                             "#ef7fff",
                                             self.db.TYPE_Line,
                                             text="#$marker_id")
        self.my_type2 = self.db.setMarkerType("kymograph_end", "#df00ff",
                                              self.db.TYPE_Normal)
        self.cp.reloadTypes()

        # add a plot widget
        self.plot = MatplotlibWidget(self)
        self.layout.addWidget(self.plot)
        self.layout.addWidget(NavigationToolbar(self.plot, self))
        self.plot.figure.canvas.mpl_connect('button_press_event',
                                            self.button_press_callback)

        # add export buttons
        layout = QtWidgets.QHBoxLayout()
        self.button_export = QtWidgets.QPushButton("Export")
        self.button_export.clicked.connect(self.export)
        layout.addWidget(self.button_export)
        self.button_export2 = QtWidgets.QPushButton("Export All")
        self.button_export2.clicked.connect(self.export2)
        layout.addWidget(self.button_export2)
        self.layout.addLayout(layout)

        # add a progress bar
        self.progressbar = QtWidgets.QProgressBar()
        self.layout.addWidget(self.progressbar)

        # connect slots
        self.signal_update_plot.connect(self.updatePlotImageEvent)
        self.signal_plot_finished.connect(self.plotFinishedEvent)

        # initialize the table
        self.updateTable()
        self.selected = None

    def button_press_callback(self, event):
        # only drag with left mouse button
        if event.button != 1:
            return
        # if the user doesn't have clicked on an axis do nothing
        if event.inaxes is None:
            return
        # get the pixel of the kymograph
        x, y = event.xdata / self.input_scale1.value(
        ), event.ydata / self.h / self.input_scale2.value()
        # jump to the frame in time
        self.cp.jumpToFrame(self.bar.image.sort_index + int(y))
        # and to the xy position
        self.cp.centerOn(*self.getLinePoint(self.bar, x))

    def cellSelected(self, row, column):
        # store the row
        self.selected = row
        # and update the plot
        self.updatePlot()

    def setTableText(self, row, column, text):
        if column == -1:
            item = self.tableWidget.verticalHeaderItem(row)
            if item is None:
                item = QtWidgets.QTableWidgetItem("")
                self.tableWidget.setVerticalHeaderItem(row, item)
        else:
            item = self.tableWidget.item(row, column)
            if item is None:
                item = QtWidgets.QTableWidgetItem("")
                self.tableWidget.setItem(row, column, item)
                if column == 2:
                    item.setFlags(QtCore.Qt.ItemIsSelectable
                                  | QtCore.Qt.ItemIsEnabled)
        item.setText(str(text))

    def updateTable(self):
        self.updating = True
        bars = self.db.getLines(type=self.my_type)
        self.bars = [bar for bar in bars]
        self.bar_dict = {}
        self.tableWidget.setRowCount(bars.count())
        self.last_image_id = None
        for idx, bar in enumerate(bars):
            self.updateRow(idx)
            self.bar_dict[bar.id] = idx
        self.updating = False

    def updateRow(self, idx):
        bar = self.bars[idx]
        self.setTableText(idx, -1, "#%d" % bar.id)
        self.setTableText(idx, 0, bar.length())

    def getLinePoint(self, line, percentage):
        x1 = line.x1
        x2 = line.x2
        y1 = line.y1
        y2 = line.y2
        if self.mirror:
            y1, y2 = y2, y1
        w = x2 - x1
        h = y2 - y1
        length = np.sqrt(w**2 + h**2)
        if self.mirror:
            percentage = length - percentage
        return x1 + w * percentage / length, y1 + h * percentage / length

    def getLine(self, image, line, height, image_entry=None):
        x1 = line.x1
        x2 = line.x2
        y1 = line.y1
        y2 = line.y2
        if self.mirror:
            y1, y2 = y2, y1
        w = x2 - x1
        h = y2 - y1
        length = np.sqrt(w**2 + h**2)
        w2 = h / length
        h2 = -w / length

        if image_entry and image_entry.offset:
            offx, offy = image_entry.offset.x, image_entry.offset.y
        else:
            offx, offy = 0, 0
        x1 -= offx - self.start_offx
        y1 -= offy - self.start_offy

        datas = []
        for j in np.arange(0, self.h) - self.h / 2. + 0.5:
            data = []
            for i in np.linspace(0, 1, np.ceil(length)):
                x = x1 + w * i + w2 * j
                y = y1 + h * i + h2 * j
                xp = x - np.floor(x)
                yp = y - np.floor(y)
                v = np.dot(
                    np.array([[1 - yp, yp]]).T, np.array([[1 - xp, xp]]))
                if len(image.shape) == 3:
                    data.append(
                        np.sum(image[int(y):int(y) + 2,
                                     int(x):int(x) + 2, :] * v[:, :, None],
                               axis=(0, 1),
                               dtype=image.dtype))
                else:
                    data.append(
                        np.sum(image[int(y):int(y) + 2,
                                     int(x):int(x) + 2] * v,
                               dtype=image.dtype))
            datas.append(data)

        if self.mirror:
            return np.array(datas)[:, ::-1]
        return np.array(datas)[::-1, :]

    def updatePlot(self):
        if self.selected is None:
            return
        self.n = -1
        self.terminate()
        self.mirror = False
        if self.db.getOption("rotation") == 180:
            self.mirror = True

        self.bar = self.bars[self.selected]
        self.plot.axes.clear()
        image_start = self.bar.image
        if image_start.offset:
            self.start_offx, self.start_offy = image_start.offset.x, image_start.offset.y
        else:
            self.start_offx, self.start_offy = 0, 0
        self.h = self.input_width.value()
        if int(self.input_count.value()) == 0:
            image = self.bar.image.sort_index
            end_marker = self.db.table_marker.select().where(
                self.db.table_marker.type == self.my_type2).join(
                    self.db.table_image).where(
                        self.db.table_image.sort_index > image).limit(1)
            self.n = end_marker[0].image.sort_index - image
        else:
            self.n = int(self.input_count.value())
        self.progressbar.setRange(0, self.n - 1)
        data = image_start.data
        line_cut = self.getLine(data, self.bar, self.h, image_start)
        self.w = line_cut.shape[1]

        if len(data.shape) == 3:
            self.current_data = np.zeros(
                (self.h * self.n, self.w, data.shape[2]), dtype=line_cut.dtype)
        else:
            self.current_data = np.zeros((self.h * self.n, self.w),
                                         dtype=line_cut.dtype)
        self.current_data[0:self.h, :] = line_cut

        extent = (0, self.current_data.shape[1] * self.input_scale1.value(),
                  self.current_data.shape[0] * self.input_scale2.value(), 0)
        if self.input_colormap.currentText() != "None":
            if len(self.current_data.shape) == 3:
                data_gray = np.dot(self.current_data[..., :3],
                                   [0.299, 0.587, 0.114])
                self.image_plot = self.plot.axes.imshow(
                    data_gray,
                    cmap=self.input_colormap.currentText(),
                    extent=extent)
            else:
                self.image_plot = self.plot.axes.imshow(
                    self.current_data,
                    cmap=self.input_colormap.currentText(),
                    extent=extent)
        else:
            self.image_plot = self.plot.axes.imshow(self.current_data,
                                                    cmap="gray",
                                                    extent=extent)
        self.plot.axes.set_xlabel(u"distance (µm)")
        self.plot.axes.set_ylabel("time (s)")
        self.plot.figure.tight_layout()
        self.plot.draw()

        self.last_update = time.time()

        self.run_threaded(image_start.sort_index + 1, self.run)

    def updatePlotImageEvent(self):
        t = time.time()
        if t - self.last_update < 0.1 and self.index < self.n - 1:
            return
        self.last_update = t
        if self.image_plot:
            if len(self.current_data.shape
                   ) == 3 and self.input_colormap.currentText() != "None":
                data_gray = np.dot(self.current_data[..., :3],
                                   [0.299, 0.587, 0.114])
                self.image_plot.set_data(data_gray)
            else:
                self.image_plot.set_data(self.current_data)
        self.plot.draw()
        self.progressbar.setValue(self.index)

    def run(self, start_frame=0):
        for index, image in enumerate(self.db.getImageIterator(start_frame)):
            index += 1
            self.index = index
            line_cut = self.getLine(image.data, self.bar, self.h, image)
            self.current_data[index * self.h:(index + 1) *
                              self.h, :] = line_cut
            self.signal_update_plot.emit()
            if index >= self.n - 1 or self.cp.stop:
                self.signal_plot_finished.emit()
                break

    def plotFinishedEvent(self):
        if self.exporting:
            self.export()
            self.exporting_index += 1
            if self.exporting_index < len(self.bars):
                self.cellSelected(self.exporting_index, 0)
            else:
                self.exporting_index = 0
                self.exporting = False

    def export(self):
        filename = "kymograph%d.%s"
        # convert to grayscale if it is a color image that should be saved with a colormap
        if len(self.current_data.shape
               ) == 3 and self.input_colormap.currentText() != "None":
            data_gray = np.dot(self.current_data[..., :3],
                               [0.299, 0.587, 0.114])
        # if not just keep it
        else:
            data_gray = self.current_data
        # save the data as a numpy file
        np.savez(filename % (self.bar.id, "npz"), data_gray)
        # get the colormap
        cmap = self.input_colormap.currentText()
        if cmap == "None":
            cmap = "gray"
        # save the kymograph as an image
        plt.imsave(filename % (self.bar.id, "png"), data_gray, cmap=cmap)
        # print a log in the console
        print("Exported", filename % (self.bar.id, "npz"))

    def export2(self):
        self.exporting_index = 0
        self.exporting = True
        self.cellSelected(self.exporting_index, 0)

    def markerMoveEvent(self, marker):
        if marker.type == self.my_type:
            row = self.bar_dict[marker.id]
            self.bars[row] = marker
            self.tableWidget.selectRow(row)
            self.updateRow(row)
            self.selected = row
            self.updatePlot()

    def markerAddEvent(self, entry):
        self.updateTable()

    def markerRemoveEvent(self, entry):
        self.updateTable()

    def buttonPressedEvent(self):
        self.show()
Example #4
0
    def __init__(self, *args, **kwargs):
        clickpoints.Addon.__init__(self, *args, **kwargs)

        # Check if the marker type is present
        if not self.db.getMarkerType("DM_horizon"):
            self.db.setMarkerType("DM_horizon", [0, 255, 255],
                                  self.db.TYPE_Normal)
            self.cp.reloadTypes()

        if not self.db.getMarkerType("DM_to_cam"):
            self.db.setMarkerType("DM_to_cam", [255, 255, 0],
                                  self.db.TYPE_Normal)
            self.cp.reloadTypes()

        if not self.db.getMarkerType("DM_between"):
            self.db.setMarkerType("DM_between", [255, 0, 255],
                                  self.db.TYPE_Line)
            self.cp.reloadTypes()

        if not self.db.getMarkerType("DM_area"):
            self.db.setMarkerType("DM_area", [0, 255, 0], self.db.TYPE_Normal)
            self.cp.reloadTypes()

        if not self.db.getMarkerType("DM_scalebox"):
            self.db.setMarkerType("DM_scalebox", [0, 255, 255],
                                  self.db.TYPE_Normal)
            self.cp.reloadTypes()

        if not self.db.getMarkerType("DM_GPS"):
            self.db.setMarkerType("DM_GPS", [128, 128, 255],
                                  self.db.TYPE_Normal)
            self.cp.reloadTypes()

        # TODO: move to parameter
        self.scalebox_dim = 10  # in meter
        self.scalebox_dict = dict()

        # Check if mask type is present
        if not self.db.getMaskType("area"):
            self.db.setMaskType("area", color='#00FF00')
            self.cp.reloadMaskTypes()

        # store options
        self.addOption(key='DM_last_camera',
                       default=None,
                       hidden=True,
                       value_type='int')
        self.addOption(key='DM_focallength_mm',
                       default=None,
                       hidden=True,
                       value_type='float')
        self.addOption(key='DM_fov_h_deg',
                       default=None,
                       hidden=True,
                       value_type='float')
        self.addOption(key='DM_fov_v_deg',
                       default=None,
                       hidden=True,
                       value_type='float')
        self.addOption(key='DM_sensor_w_mm',
                       default=None,
                       hidden=True,
                       value_type='float')
        self.addOption(key='DM_sensor_h_mm',
                       default=None,
                       hidden=True,
                       value_type='float')
        self.addOption(key='DM_img_w_px',
                       default=None,
                       hidden=True,
                       value_type='int')
        self.addOption(key='DM_img_h_px',
                       default=None,
                       hidden=True,
                       value_type='int')
        self.addOption(key='DM_offset_x_px',
                       default=None,
                       hidden=True,
                       value_type='float')
        self.addOption(key='DM_offset_y_px',
                       default=None,
                       hidden=True,
                       value_type='float')

        self.addOption(key='DM_cam_elevation',
                       default=20,
                       hidden=True,
                       value_type='float')
        self.addOption(key='DM_plane_elevation',
                       default=0,
                       hidden=True,
                       value_type='float')

        # get json files if available else create defaults
        """ Default entries for Cameras """
        #region
        # atkaSPOT
        # Mobotic D12 Day as used in atkaSPOT
        MobotixM12_Day = dotdict()
        MobotixM12_Day.fov_h_deg = 45
        MobotixM12_Day.fov_v_deg = 34
        MobotixM12_Day.sensor_w_mm = None
        MobotixM12_Day.sensor_h_mm = None
        MobotixM12_Day.focallength_mm = 22
        MobotixM12_Day.img_w_px = 2048
        MobotixM12_Day.img_h_px = 1536

        MobotixM12_Night = dotdict()
        MobotixM12_Night.fov_h_deg = 45
        MobotixM12_Night.fov_v_deg = 34
        MobotixM12_Night.sensor_w_mm = None
        MobotixM12_Night.sensor_h_mm = None
        MobotixM12_Night.focallength_mm = 22
        MobotixM12_Night.img_w_px = 2048
        MobotixM12_Night.img_h_px = 1536

        CampbellMpx5 = dotdict()
        CampbellMpx5.fov_h_deg = 80
        CampbellMpx5.fov_v_deg = 65
        CampbellMpx5.sensor_w_mm = None
        CampbellMpx5.sensor_h_mm = None
        CampbellMpx5.focallength_mm = 12
        CampbellMpx5.img_w_px = 2470
        CampbellMpx5.img_h_px = 1800

        GE4000C_400mm = dotdict()
        GE4000C_400mm.fov_h_deg = None
        GE4000C_400mm.fov_v_deg = None
        GE4000C_400mm.sensor_w_mm = 36
        GE4000C_400mm.sensor_h_mm = 24
        GE4000C_400mm.focallength_mm = 400
        GE4000C_400mm.img_w_px = 4008
        GE4000C_400mm.img_h_px = 2672

        GE4000C_400mm_crop05 = dotdict()
        GE4000C_400mm_crop05.fov_h_deg = None
        GE4000C_400mm_crop05.fov_v_deg = None
        GE4000C_400mm_crop05.sensor_w_mm = 36
        GE4000C_400mm_crop05.sensor_h_mm = 24
        GE4000C_400mm_crop05.focallength_mm = 400
        GE4000C_400mm_crop05.img_w_px = 2004
        GE4000C_400mm_crop05.img_h_px = 1336

        Panasonic_DMC_G5 = dotdict()
        Panasonic_DMC_G5.fov_h_deg = None
        Panasonic_DMC_G5.fov_v_deg = None
        Panasonic_DMC_G5.sensor_w_mm = 17.3
        Panasonic_DMC_G5.sensor_h_mm = 13.0
        Panasonic_DMC_G5.focallength_mm = 14
        Panasonic_DMC_G5.img_w_px = 4608
        Panasonic_DMC_G5.img_h_px = 3456

        Canon_D10 = dotdict()
        Canon_D10.fov_h_deg = None
        Canon_D10.fov_v_deg = None
        Canon_D10.sensor_w_mm = 6.17
        Canon_D10.sensor_h_mm = 4.55
        Canon_D10.focallength_mm = 6.2
        Canon_D10.img_w_px = 4000
        Canon_D10.img_h_px = 3000

        # add all cameras to one dictionary
        cam_dict = dotdict()
        cam_dict['MobotixM12_Day'] = MobotixM12_Day
        cam_dict['MobotixM12_Night'] = MobotixM12_Night
        cam_dict['CampbellMpx5'] = CampbellMpx5
        cam_dict['GE4000C_400mm'] = GE4000C_400mm
        cam_dict['GE4000C_400mm_crop05'] = GE4000C_400mm_crop05
        cam_dict['Panasonic_DMC_G5'] = Panasonic_DMC_G5
        cam_dict['Canon_D10'] = Canon_D10
        # endregion

        camera_json = os.path.join(os.path.dirname(__file__), 'camera.json')
        if not os.path.exists(camera_json):
            print(
                "DistanceMeasure Addon: no default camera.json found - creating ..."
            )
            with open(camera_json, 'w') as fd:
                json.dump(cam_dict, fd, indent=4, sort_keys=True)
        else:
            # read from json file
            print("DistanceMeasure Addon: loading camera.json")
            with open(camera_json, 'r') as fd:
                self.cam_dict = json.load(fd)

        ## Widget
        # set the title and layout
        self.setWindowTitle("Distance - Config")
        self.setWindowIcon(qta.icon("fa.map-signs"))
        # self.setMinimumWidth(400)
        # self.setMinimumHeight(200)
        self.layout = QtWidgets.QGridLayout(self)

        ## camera region
        # use a groupbox for camera parameters
        self.camera_groupbox = QtWidgets.QGroupBox("Camera")
        self.camera_groupbox.setSizePolicy(
            QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed,
                                  QtWidgets.QSizePolicy.Fixed))
        self.layout.addWidget(self.camera_groupbox, 0, 0)

        # add a grid layout for elements
        self.camera_layout = QtWidgets.QVBoxLayout()
        self.camera_groupbox.setLayout(self.camera_layout)

        # combo box to select camera models stored in camera.json
        self.cameraComboBox = AddQComboBox(self.camera_layout,
                                           'Model:',
                                           values=cam_dict.keys())
        self.cameraComboBox.currentIndexChanged.connect(
            self.getCameraParametersByJson)

        self.openFile = AddQOpenFileChoose(self.camera_layout,
                                           "Input",
                                           "",
                                           file_type="*.*")
        self.openFile.textChanged.connect(self.getCameraParametersByCT)

        # self.cameraButton = QtWidgets.QPushButton('Load Cam')
        # self.cameraButton.clicked.connect(self.loadCamerFromFile)
        # self.camera_layout.addWidget(self.cameraButton)

        # self.cameraComboBox.setCurrentIndex(5)
        # self.cameraComboBox.addItems(cam_dict.keys())

        self.cbProjection = AddQComboBox(
            self.camera_layout,
            "Camera Projection:",
            values=["Rectilinear", "Cylindrical", "Equirectangular"])

        self.leFocallength = AddQLineEdit(self.camera_layout,
                                          "f (mm):",
                                          editwidth=120)
        self.leImage_width = AddQLineEdit(self.camera_layout,
                                          "image width (px):",
                                          editwidth=120)
        self.leImage_height = AddQLineEdit(self.camera_layout,
                                           "image height (px):",
                                           editwidth=120)
        self.leSensor_width = AddQLineEdit(self.camera_layout,
                                           "sensor width (mm):",
                                           editwidth=120)
        self.leSensor_height = AddQLineEdit(self.camera_layout,
                                            "sensor height (mm):",
                                            editwidth=120)
        self.leFOV_horizontal = AddQLineEdit(self.camera_layout,
                                             "FOV horizontal (deg):",
                                             editwidth=120)
        self.leFOV_vertical = AddQLineEdit(self.camera_layout,
                                           "FOV vertical (deg):",
                                           editwidth=120)
        self.leOffsetX = AddQLineEdit(self.camera_layout,
                                      "center offset x (px):",
                                      "0.",
                                      editwidth=120)
        self.leOffsetY = AddQLineEdit(self.camera_layout,
                                      "center offset y (px):",
                                      "0.",
                                      editwidth=120)

        ## position region
        self.position_groupbox = QtWidgets.QGroupBox("Position")
        self.position_groupbox.setSizePolicy(
            QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed,
                                  QtWidgets.QSizePolicy.Fixed))

        self.layout.addWidget(self.position_groupbox, 1, 0)
        # add a grid layout for elements
        self.position_layout = QtWidgets.QVBoxLayout()
        self.position_groupbox.setLayout(self.position_layout)

        self.leCamElevation = AddQLineEdit(self.position_layout,
                                           "camera elevation (m):",
                                           editwidth=120,
                                           value='25')
        self.lePlaneElevation = AddQLineEdit(self.position_layout,
                                             "plane elevation (m):",
                                             editwidth=120,
                                             value='0')
        self.leCamTilt = AddQLineEdit(self.position_layout,
                                      "camera tilt:",
                                      editwidth=120,
                                      value=None)
        self.leCamRoll = AddQLineEdit(self.position_layout,
                                      "camera roll:",
                                      editwidth=120,
                                      value=None)
        self.leCamPan = AddQLineEdit(self.position_layout,
                                     "camera pan:",
                                     editwidth=120,
                                     value=None)
        self.leCamLat = AddQLineEdit(self.position_layout,
                                     "camera latitude:",
                                     editwidth=120)
        self.leCamLon = AddQLineEdit(self.position_layout,
                                     "camera longitude:",
                                     editwidth=120)
        self.leCamPosX = AddQLineEdit(self.position_layout,
                                      "camera position X:",
                                      editwidth=120)
        self.leCamPosY = AddQLineEdit(self.position_layout,
                                      "camera position Y:",
                                      editwidth=120)

        for c in self.position_groupbox.children():
            if hasattr(c, "editingFinished"):
                c.editingFinished.connect(self.update)
        for c in self.camera_groupbox.children():
            if hasattr(c, "editingFinished"):
                c.editingFinished.connect(self.update)
        # get current frame
        cframe = self.cp.getCurrentFrame()

        # retrieve data
        qimg = self.db.getImage(frame=cframe)
        print(
            os.path.isfile(
                os.path.join(qimg.path.path,
                             os.path.splitext(qimg.filename)[0], ".ct")))

        # fill context menu from stored options if available
        if self.getOption('DM_last_camera'):
            # load from DB
            self.cameraComboBox.setCurrentIndex(
                self.getOption('DM_last_camera'))
            self.getCameraParametersByDB()
            self.getPostionParametersByDB()
            self.updateCameraParameters()
        elif os.path.isfile(
                os.path.join(qimg.path.path,
                             os.path.splitext(qimg.filename)[0], ".ct")):
            self.getCameraParametersByCT(
                os.path.isfile(
                    os.path.join(qimg.path.path,
                                 os.path.splitext(qimg.filename)[0], ".ct")))
            self.updateCameraParameters()
        else:
            # load from json
            self.getCameraParametersByJson()
            self.updateCameraParameters()

        ## display region
        self.display_groupbox = QtWidgets.QGroupBox("Display and Evaluation")
        self.display_groupbox.setSizePolicy(
            QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed,
                                  QtWidgets.QSizePolicy.Fixed))
        self.layout.addWidget(self.display_groupbox, 2, 0)

        self.display_layout = QtWidgets.QVBoxLayout()
        self.display_groupbox.setLayout(self.display_layout)

        self.cbCalcArea = AddQCheckBox(self.display_layout,
                                       "calculate marked area",
                                       checked=False,
                                       strech=False)
        self.cbCalcArea.setToolTip(
            'NOTE: LUT mode area calculation assumes a cylindrical projection with a straight and horizontal horizon'
        )

        self.cbFitParameters = AddQCheckBox(self.display_layout,
                                            "fit camera parameters",
                                            checked=False,
                                            strech=False)

        self.sbScaleboxDim = AddQSpinBox(self.display_layout,
                                         "scale Box size",
                                         value=10,
                                         strech=False)

        self.cbShowHorizon = AddQCheckBox(self.display_layout,
                                          "display horizon",
                                          checked=True,
                                          strech=False)
        self.horizon_line = None

        ## projection region
        self.projection_groupbox = QtWidgets.QGroupBox("Projection")
        self.projection_groupbox.setSizePolicy(
            QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding,
                                  QtWidgets.QSizePolicy.Expanding))
        self.layout.addWidget(self.projection_groupbox, 0, 1, 6, 6)

        self.projection_layout = QtWidgets.QVBoxLayout()
        self.projection_groupbox.setLayout(self.projection_layout)

        self.pltWidget = matplotlibwidget.MatplotlibWidget()
        self.projection_layout.addWidget(self.pltWidget)

        self.leExtent = AddQLineEdit(self.projection_layout, 'Extent', '')
        self.leScaling = AddQLineEdit(self.projection_layout, 'Scaling', '')

        self.pbRefreshProjection = QtWidgets.QPushButton('Refresh')
        self.pbRefreshProjection.clicked.connect(
            self.pushbutton_refreshprojction)
        self.projection_layout.addWidget(self.pbRefreshProjection)

        ## stretch area
        self.layout.addItem(
            QtWidgets.QSpacerItem(1, 1, QtWidgets.QSizePolicy.Minimum,
                                  QtWidgets.QSizePolicy.Expanding), 4, 0)

        self.pushbutton_ok = QtWidgets.QPushButton("Ok")
        self.pushbutton_ok.setSizePolicy(QtWidgets.QSizePolicy.Fixed,
                                         QtWidgets.QSizePolicy.Fixed)
        self.layout.addWidget(self.pushbutton_ok, 5, 0)
        self.pushbutton_ok.clicked.connect(self.run)
Example #5
0
class Addon(clickpoints.Addon):
    camera = None
    cam_dict = None
    initialized = False

    def __init__(self, *args, **kwargs):
        clickpoints.Addon.__init__(self, *args, **kwargs)

        # Check if the marker type is present
        if not self.db.getMarkerType("DM_horizon"):
            self.db.setMarkerType("DM_horizon", [0, 255, 255],
                                  self.db.TYPE_Normal)
            self.cp.reloadTypes()

        if not self.db.getMarkerType("DM_to_cam"):
            self.db.setMarkerType("DM_to_cam", [255, 255, 0],
                                  self.db.TYPE_Normal)
            self.cp.reloadTypes()

        if not self.db.getMarkerType("DM_between"):
            self.db.setMarkerType("DM_between", [255, 0, 255],
                                  self.db.TYPE_Line)
            self.cp.reloadTypes()

        if not self.db.getMarkerType("DM_area"):
            self.db.setMarkerType("DM_area", [0, 255, 0], self.db.TYPE_Normal)
            self.cp.reloadTypes()

        if not self.db.getMarkerType("DM_scalebox"):
            self.db.setMarkerType("DM_scalebox", [0, 255, 255],
                                  self.db.TYPE_Normal)
            self.cp.reloadTypes()

        if not self.db.getMarkerType("DM_GPS"):
            self.db.setMarkerType("DM_GPS", [128, 128, 255],
                                  self.db.TYPE_Normal)
            self.cp.reloadTypes()

        # TODO: move to parameter
        self.scalebox_dim = 10  # in meter
        self.scalebox_dict = dict()

        # Check if mask type is present
        if not self.db.getMaskType("area"):
            self.db.setMaskType("area", color='#00FF00')
            self.cp.reloadMaskTypes()

        # store options
        self.addOption(key='DM_last_camera',
                       default=None,
                       hidden=True,
                       value_type='int')
        self.addOption(key='DM_focallength_mm',
                       default=None,
                       hidden=True,
                       value_type='float')
        self.addOption(key='DM_fov_h_deg',
                       default=None,
                       hidden=True,
                       value_type='float')
        self.addOption(key='DM_fov_v_deg',
                       default=None,
                       hidden=True,
                       value_type='float')
        self.addOption(key='DM_sensor_w_mm',
                       default=None,
                       hidden=True,
                       value_type='float')
        self.addOption(key='DM_sensor_h_mm',
                       default=None,
                       hidden=True,
                       value_type='float')
        self.addOption(key='DM_img_w_px',
                       default=None,
                       hidden=True,
                       value_type='int')
        self.addOption(key='DM_img_h_px',
                       default=None,
                       hidden=True,
                       value_type='int')
        self.addOption(key='DM_offset_x_px',
                       default=None,
                       hidden=True,
                       value_type='float')
        self.addOption(key='DM_offset_y_px',
                       default=None,
                       hidden=True,
                       value_type='float')

        self.addOption(key='DM_cam_elevation',
                       default=20,
                       hidden=True,
                       value_type='float')
        self.addOption(key='DM_plane_elevation',
                       default=0,
                       hidden=True,
                       value_type='float')

        # get json files if available else create defaults
        """ Default entries for Cameras """
        #region
        # atkaSPOT
        # Mobotic D12 Day as used in atkaSPOT
        MobotixM12_Day = dotdict()
        MobotixM12_Day.fov_h_deg = 45
        MobotixM12_Day.fov_v_deg = 34
        MobotixM12_Day.sensor_w_mm = None
        MobotixM12_Day.sensor_h_mm = None
        MobotixM12_Day.focallength_mm = 22
        MobotixM12_Day.img_w_px = 2048
        MobotixM12_Day.img_h_px = 1536

        MobotixM12_Night = dotdict()
        MobotixM12_Night.fov_h_deg = 45
        MobotixM12_Night.fov_v_deg = 34
        MobotixM12_Night.sensor_w_mm = None
        MobotixM12_Night.sensor_h_mm = None
        MobotixM12_Night.focallength_mm = 22
        MobotixM12_Night.img_w_px = 2048
        MobotixM12_Night.img_h_px = 1536

        CampbellMpx5 = dotdict()
        CampbellMpx5.fov_h_deg = 80
        CampbellMpx5.fov_v_deg = 65
        CampbellMpx5.sensor_w_mm = None
        CampbellMpx5.sensor_h_mm = None
        CampbellMpx5.focallength_mm = 12
        CampbellMpx5.img_w_px = 2470
        CampbellMpx5.img_h_px = 1800

        GE4000C_400mm = dotdict()
        GE4000C_400mm.fov_h_deg = None
        GE4000C_400mm.fov_v_deg = None
        GE4000C_400mm.sensor_w_mm = 36
        GE4000C_400mm.sensor_h_mm = 24
        GE4000C_400mm.focallength_mm = 400
        GE4000C_400mm.img_w_px = 4008
        GE4000C_400mm.img_h_px = 2672

        GE4000C_400mm_crop05 = dotdict()
        GE4000C_400mm_crop05.fov_h_deg = None
        GE4000C_400mm_crop05.fov_v_deg = None
        GE4000C_400mm_crop05.sensor_w_mm = 36
        GE4000C_400mm_crop05.sensor_h_mm = 24
        GE4000C_400mm_crop05.focallength_mm = 400
        GE4000C_400mm_crop05.img_w_px = 2004
        GE4000C_400mm_crop05.img_h_px = 1336

        Panasonic_DMC_G5 = dotdict()
        Panasonic_DMC_G5.fov_h_deg = None
        Panasonic_DMC_G5.fov_v_deg = None
        Panasonic_DMC_G5.sensor_w_mm = 17.3
        Panasonic_DMC_G5.sensor_h_mm = 13.0
        Panasonic_DMC_G5.focallength_mm = 14
        Panasonic_DMC_G5.img_w_px = 4608
        Panasonic_DMC_G5.img_h_px = 3456

        Canon_D10 = dotdict()
        Canon_D10.fov_h_deg = None
        Canon_D10.fov_v_deg = None
        Canon_D10.sensor_w_mm = 6.17
        Canon_D10.sensor_h_mm = 4.55
        Canon_D10.focallength_mm = 6.2
        Canon_D10.img_w_px = 4000
        Canon_D10.img_h_px = 3000

        # add all cameras to one dictionary
        cam_dict = dotdict()
        cam_dict['MobotixM12_Day'] = MobotixM12_Day
        cam_dict['MobotixM12_Night'] = MobotixM12_Night
        cam_dict['CampbellMpx5'] = CampbellMpx5
        cam_dict['GE4000C_400mm'] = GE4000C_400mm
        cam_dict['GE4000C_400mm_crop05'] = GE4000C_400mm_crop05
        cam_dict['Panasonic_DMC_G5'] = Panasonic_DMC_G5
        cam_dict['Canon_D10'] = Canon_D10
        # endregion

        camera_json = os.path.join(os.path.dirname(__file__), 'camera.json')
        if not os.path.exists(camera_json):
            print(
                "DistanceMeasure Addon: no default camera.json found - creating ..."
            )
            with open(camera_json, 'w') as fd:
                json.dump(cam_dict, fd, indent=4, sort_keys=True)
        else:
            # read from json file
            print("DistanceMeasure Addon: loading camera.json")
            with open(camera_json, 'r') as fd:
                self.cam_dict = json.load(fd)

        ## Widget
        # set the title and layout
        self.setWindowTitle("Distance - Config")
        self.setWindowIcon(qta.icon("fa.map-signs"))
        # self.setMinimumWidth(400)
        # self.setMinimumHeight(200)
        self.layout = QtWidgets.QGridLayout(self)

        ## camera region
        # use a groupbox for camera parameters
        self.camera_groupbox = QtWidgets.QGroupBox("Camera")
        self.camera_groupbox.setSizePolicy(
            QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed,
                                  QtWidgets.QSizePolicy.Fixed))
        self.layout.addWidget(self.camera_groupbox, 0, 0)

        # add a grid layout for elements
        self.camera_layout = QtWidgets.QVBoxLayout()
        self.camera_groupbox.setLayout(self.camera_layout)

        # combo box to select camera models stored in camera.json
        self.cameraComboBox = AddQComboBox(self.camera_layout,
                                           'Model:',
                                           values=cam_dict.keys())
        self.cameraComboBox.currentIndexChanged.connect(
            self.getCameraParametersByJson)

        self.openFile = AddQOpenFileChoose(self.camera_layout,
                                           "Input",
                                           "",
                                           file_type="*.*")
        self.openFile.textChanged.connect(self.getCameraParametersByCT)

        # self.cameraButton = QtWidgets.QPushButton('Load Cam')
        # self.cameraButton.clicked.connect(self.loadCamerFromFile)
        # self.camera_layout.addWidget(self.cameraButton)

        # self.cameraComboBox.setCurrentIndex(5)
        # self.cameraComboBox.addItems(cam_dict.keys())

        self.cbProjection = AddQComboBox(
            self.camera_layout,
            "Camera Projection:",
            values=["Rectilinear", "Cylindrical", "Equirectangular"])

        self.leFocallength = AddQLineEdit(self.camera_layout,
                                          "f (mm):",
                                          editwidth=120)
        self.leImage_width = AddQLineEdit(self.camera_layout,
                                          "image width (px):",
                                          editwidth=120)
        self.leImage_height = AddQLineEdit(self.camera_layout,
                                           "image height (px):",
                                           editwidth=120)
        self.leSensor_width = AddQLineEdit(self.camera_layout,
                                           "sensor width (mm):",
                                           editwidth=120)
        self.leSensor_height = AddQLineEdit(self.camera_layout,
                                            "sensor height (mm):",
                                            editwidth=120)
        self.leFOV_horizontal = AddQLineEdit(self.camera_layout,
                                             "FOV horizontal (deg):",
                                             editwidth=120)
        self.leFOV_vertical = AddQLineEdit(self.camera_layout,
                                           "FOV vertical (deg):",
                                           editwidth=120)
        self.leOffsetX = AddQLineEdit(self.camera_layout,
                                      "center offset x (px):",
                                      "0.",
                                      editwidth=120)
        self.leOffsetY = AddQLineEdit(self.camera_layout,
                                      "center offset y (px):",
                                      "0.",
                                      editwidth=120)

        ## position region
        self.position_groupbox = QtWidgets.QGroupBox("Position")
        self.position_groupbox.setSizePolicy(
            QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed,
                                  QtWidgets.QSizePolicy.Fixed))

        self.layout.addWidget(self.position_groupbox, 1, 0)
        # add a grid layout for elements
        self.position_layout = QtWidgets.QVBoxLayout()
        self.position_groupbox.setLayout(self.position_layout)

        self.leCamElevation = AddQLineEdit(self.position_layout,
                                           "camera elevation (m):",
                                           editwidth=120,
                                           value='25')
        self.lePlaneElevation = AddQLineEdit(self.position_layout,
                                             "plane elevation (m):",
                                             editwidth=120,
                                             value='0')
        self.leCamTilt = AddQLineEdit(self.position_layout,
                                      "camera tilt:",
                                      editwidth=120,
                                      value=None)
        self.leCamRoll = AddQLineEdit(self.position_layout,
                                      "camera roll:",
                                      editwidth=120,
                                      value=None)
        self.leCamPan = AddQLineEdit(self.position_layout,
                                     "camera pan:",
                                     editwidth=120,
                                     value=None)
        self.leCamLat = AddQLineEdit(self.position_layout,
                                     "camera latitude:",
                                     editwidth=120)
        self.leCamLon = AddQLineEdit(self.position_layout,
                                     "camera longitude:",
                                     editwidth=120)
        self.leCamPosX = AddQLineEdit(self.position_layout,
                                      "camera position X:",
                                      editwidth=120)
        self.leCamPosY = AddQLineEdit(self.position_layout,
                                      "camera position Y:",
                                      editwidth=120)

        for c in self.position_groupbox.children():
            if hasattr(c, "editingFinished"):
                c.editingFinished.connect(self.update)
        for c in self.camera_groupbox.children():
            if hasattr(c, "editingFinished"):
                c.editingFinished.connect(self.update)
        # get current frame
        cframe = self.cp.getCurrentFrame()

        # retrieve data
        qimg = self.db.getImage(frame=cframe)
        print(
            os.path.isfile(
                os.path.join(qimg.path.path,
                             os.path.splitext(qimg.filename)[0], ".ct")))

        # fill context menu from stored options if available
        if self.getOption('DM_last_camera'):
            # load from DB
            self.cameraComboBox.setCurrentIndex(
                self.getOption('DM_last_camera'))
            self.getCameraParametersByDB()
            self.getPostionParametersByDB()
            self.updateCameraParameters()
        elif os.path.isfile(
                os.path.join(qimg.path.path,
                             os.path.splitext(qimg.filename)[0], ".ct")):
            self.getCameraParametersByCT(
                os.path.isfile(
                    os.path.join(qimg.path.path,
                                 os.path.splitext(qimg.filename)[0], ".ct")))
            self.updateCameraParameters()
        else:
            # load from json
            self.getCameraParametersByJson()
            self.updateCameraParameters()

        ## display region
        self.display_groupbox = QtWidgets.QGroupBox("Display and Evaluation")
        self.display_groupbox.setSizePolicy(
            QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed,
                                  QtWidgets.QSizePolicy.Fixed))
        self.layout.addWidget(self.display_groupbox, 2, 0)

        self.display_layout = QtWidgets.QVBoxLayout()
        self.display_groupbox.setLayout(self.display_layout)

        self.cbCalcArea = AddQCheckBox(self.display_layout,
                                       "calculate marked area",
                                       checked=False,
                                       strech=False)
        self.cbCalcArea.setToolTip(
            'NOTE: LUT mode area calculation assumes a cylindrical projection with a straight and horizontal horizon'
        )

        self.cbFitParameters = AddQCheckBox(self.display_layout,
                                            "fit camera parameters",
                                            checked=False,
                                            strech=False)

        self.sbScaleboxDim = AddQSpinBox(self.display_layout,
                                         "scale Box size",
                                         value=10,
                                         strech=False)

        self.cbShowHorizon = AddQCheckBox(self.display_layout,
                                          "display horizon",
                                          checked=True,
                                          strech=False)
        self.horizon_line = None

        ## projection region
        self.projection_groupbox = QtWidgets.QGroupBox("Projection")
        self.projection_groupbox.setSizePolicy(
            QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding,
                                  QtWidgets.QSizePolicy.Expanding))
        self.layout.addWidget(self.projection_groupbox, 0, 1, 6, 6)

        self.projection_layout = QtWidgets.QVBoxLayout()
        self.projection_groupbox.setLayout(self.projection_layout)

        self.pltWidget = matplotlibwidget.MatplotlibWidget()
        self.projection_layout.addWidget(self.pltWidget)

        self.leExtent = AddQLineEdit(self.projection_layout, 'Extent', '')
        self.leScaling = AddQLineEdit(self.projection_layout, 'Scaling', '')

        self.pbRefreshProjection = QtWidgets.QPushButton('Refresh')
        self.pbRefreshProjection.clicked.connect(
            self.pushbutton_refreshprojction)
        self.projection_layout.addWidget(self.pbRefreshProjection)

        ## stretch area
        self.layout.addItem(
            QtWidgets.QSpacerItem(1, 1, QtWidgets.QSizePolicy.Minimum,
                                  QtWidgets.QSizePolicy.Expanding), 4, 0)

        self.pushbutton_ok = QtWidgets.QPushButton("Ok")
        self.pushbutton_ok.setSizePolicy(QtWidgets.QSizePolicy.Fixed,
                                         QtWidgets.QSizePolicy.Fixed)
        self.layout.addWidget(self.pushbutton_ok, 5, 0)
        self.pushbutton_ok.clicked.connect(self.run)

        #self.db.addOption('DM_current_cam',self.cameraComboBox.getCurrentIndex())

    def getPostionParametersByDB(self):
        """
        load position parameters
        """
        self.leCamElevation.setText(
            getNumber(self.getOption('DM_cam_elevation'), '%.2f'))
        self.lePlaneElevation.setText(
            getNumber(self.getOption('DM_plane_elevation'), '%.2f'))

    def getCameraParametersByDB(self):
        """
        load camera parameters from last run stored in DB
        """

        # set cam parameters
        self.leFocallength.setText("%.2f" %
                                   self.getOption('DM_focallength_mm'))
        self.leImage_width.setText("%d" % self.getOption('DM_img_w_px'))
        self.leImage_height.setText("%d" % self.getOption('DM_img_h_px'))
        self.leSensor_width.setText(
            getNumber(self.getOption('DM_sensor_w_mm'), "%.2f"))
        self.leSensor_height.setText(
            getNumber(self.getOption('DM_sensor_h_mm'), "%.2f"))
        self.leFOV_horizontal.setText(
            getNumber(self.getOption('DM_fov_h_deg'), "%.2f"))
        self.leFOV_vertical.setText(
            getNumber(self.getOption('DM_fov_v_deg'), "%.2f"))
        self.leOffsetX.setText(
            getNumber(self.getOption('DM_offset_x_px'), "%.2f"))
        self.leOffsetY.setText(
            getNumber(self.getOption('DM_offset_y_px'), "%.2f"))

    def getCameraParametersByJson(self):
        """
        insert camera parameters from camera.json into gui
        """

        self.selected_cam_name = self.cameraComboBox.itemText(
            self.cameraComboBox.currentIndex())
        print("Selected Cam:", self.selected_cam_name)

        # store to options
        self.setOption('DM_last_camera', self.cameraComboBox.currentIndex())

        cam_by_dict = dotdict(self.cam_dict[self.selected_cam_name])

        def getNumber(input, format):
            try:
                return (format % input)
            except TypeError:
                return "None"

        # set cam parameters in GUI
        self.leFocallength.setText("%.2f" % cam_by_dict['focallength_mm'])
        self.leImage_width.setText("%d" % cam_by_dict['img_w_px'])
        self.leImage_height.setText("%d" % cam_by_dict['img_h_px'])
        self.leSensor_width.setText(
            getNumber(cam_by_dict['sensor_w_mm'], "%.2f"))
        self.leSensor_height.setText(
            getNumber(cam_by_dict['sensor_h_mm'], "%.2f"))
        self.leFOV_horizontal.setText(
            getNumber(cam_by_dict['fov_h_deg'], "%.2f"))
        self.leFOV_vertical.setText(getNumber(cam_by_dict['fov_v_deg'],
                                              "%.2f"))

    def getCameraParametersByCT(self, ct_file):
        cam = ct.load_camera(ct_file)
        cam_by_dict = {}

        cam_by_dict['img_w_px'] = cam.image_width_px
        cam_by_dict['img_h_px'] = cam.image_height_px

        cam_by_dict['offset_x_px'] = cam.center_x_px - cam.image_width_px / 2
        cam_by_dict['offset_y_px'] = cam.center_y_px - cam.image_height_px / 2

        cam_by_dict['sensor_w_mm'] = cam.sensor_width_mm
        cam_by_dict['sensor_h_mm'] = cam.sensor_height_mm

        cam_by_dict['fov_h_deg'] = np.rad2deg(
            np.arctan2(cam.image_width_px / 2, cam.focallength_x_px) * 2)
        cam_by_dict['fov_v_deg'] = np.rad2deg(
            np.arctan2(cam.image_height_px / 2, cam.focallength_y_px) * 2)

        cam_by_dict[
            'focallength_mm'] = cam.focallength_x_px * cam.sensor_width_mm / cam.image_width_px

        # set cam parameters in GUI
        self.leFocallength.setText("%.2f" % cam_by_dict['focallength_mm'])
        self.leImage_width.setText("%d" % cam_by_dict['img_w_px'])
        self.leImage_height.setText("%d" % cam_by_dict['img_h_px'])
        self.leSensor_width.setText(
            getNumber(cam_by_dict['sensor_w_mm'], "%.2f"))
        self.leSensor_height.setText(
            getNumber(cam_by_dict['sensor_h_mm'], "%.2f"))
        self.leFOV_horizontal.setText(
            getNumber(cam_by_dict['fov_h_deg'], "%.2f"))
        self.leFOV_vertical.setText(getNumber(cam_by_dict['fov_v_deg'],
                                              "%.2f"))

        self.leOffsetX.setText(getNumber(cam_by_dict['offset_x_px'], "%.2f"))
        self.leOffsetY.setText(getNumber(cam_by_dict['offset_y_px'], "%.2f"))

        self.leCamElevation.setText(getNumber(cam.elevation_m, "%.2f"))
        self.leCamPan.setText(getNumber(cam.heading_deg, "%.2f"))
        self.leCamTilt.setText(getNumber(cam.tilt_deg, "%.2f"))
        self.leCamRoll.setText(getNumber(cam.roll_deg, "%.2f"))

        self.leCamLat.setText(getNumber(cam.gps_lat, "%.2f"))
        self.leCamLon.setText(getNumber(cam.gps_lon, "%.2f"))

        self.leCamPosX.setText(getNumber(cam.pos_x_m, "%.2f"))
        self.leCamPosY.setText(getNumber(cam.pos_y_m, "%.2f"))

        self.updateCameraParameters()

    def calcSensorDimensionsFromFOV(self):
        self.cam.sensor_w_mm = utilFOVToSensor(self.cam.fov_h_deg,
                                               self.cam.focallength_mm)
        self.cam.sensor_h_mm = utilFOVToSensor(self.cam.fov_v_deg,
                                               self.cam.focallength_mm)

    def updateCameraParameters(self):
        """
        update camera dictionary for calculation - uses potentially user modified data from gui
        """

        # update current cam parameters
        self.cam = dotdict()
        self.cam.fov_h_deg = getFloat(self.leFOV_horizontal.text())
        self.cam.fov_v_deg = getFloat(self.leFOV_vertical.text())
        self.cam.sensor_w_mm = getFloat(self.leSensor_width.text())
        self.cam.sensor_h_mm = getFloat(self.leSensor_height.text())
        self.cam.focallength_mm = getFloat(self.leFocallength.text())
        self.cam.img_w_px = getInteger(self.leImage_width.text())
        self.cam.img_h_px = getInteger(self.leImage_height.text())
        self.cam.center_x_px = self.cam.img_w_px / 2. + getFloat(
            self.leOffsetX.text())
        self.cam.center_y_px = self.cam.img_h_px / 2. + getFloat(
            self.leOffsetY.text())

        self.cam.heading_deg = getFloat(self.leCamPan.text())
        self.cam.tilt_deg = getFloat(self.leCamTilt.text())
        self.cam.roll_deg = getFloat(self.leCamRoll.text())

        self.cam.elevation_m = getFloat(self.leCamElevation.text())
        self.cam.pos_x_m = getFloat(self.leCamPosX.text())
        self.cam.pos_y_m = getFloat(self.leCamPosY.text())

        self.cam.projection = self.cbProjection.itemText(
            self.cbProjection.currentIndex())

        self.cam.gps_lat = getFloat(self.leCamLat.text())
        self.cam.gps_lon = getFloat(self.leCamLon.text())

        if self.cam.sensor_h_mm is None or self.cam.sensor_w_mm is None:
            self.calcSensorDimensionsFromFOV()

        # save parameters as options to DB
        self.setOption('DM_focallength_mm', self.cam.focallength_mm)
        self.setOption('DM_fov_h_deg', self.cam.fov_h_deg)
        self.setOption('DM_fov_v_deg', self.cam.fov_v_deg)
        self.setOption('DM_sensor_w_mm', self.cam.sensor_w_mm)
        self.setOption('DM_sensor_h_mm', self.cam.sensor_h_mm)
        self.setOption('DM_img_w_px', self.cam.img_w_px)
        self.setOption('DM_img_h_px', self.cam.img_h_px)
        self.setOption('DM_offset_x_px', getFloat(self.leOffsetX.text()))
        self.setOption('DM_offset_y_px', getFloat(self.leOffsetY.text()))

        print("Camera:")
        print(json.dumps(self.cam, indent=4, sort_keys=True))

        self.position = dotdict()
        self.position.cam_elevation = getFloat(self.leCamElevation.text())
        self.position.plane_elevation = getFloat(self.lePlaneElevation.text())

        # save parameters to options
        self.setOption('DM_cam_elevation', self.position.cam_elevation)
        self.setOption('DM_plane_elevation', self.position.plane_elevation)

        print("Position:")
        print(json.dumps(self.position, indent=4, sort_keys=True))

        print(self.cam)

        # update camera parameters
        # self.camera = ct.CameraTransform(self.cam.focallength_mm, [self.cam.sensor_w_mm, self.cam.sensor_h_mm],[self.cam.img_w_px, self.cam.img_h_px])
        orientation = ct.SpatialOrientation(heading_deg=self.cam.heading_deg,
                                            tilt_deg=self.cam.tilt_deg,
                                            roll_deg=self.cam.roll_deg,
                                            elevation_m=self.cam.elevation_m,
                                            pos_x_m=self.cam.pos_x_m,
                                            pos_y_m=self.cam.pos_y_m)
        if self.cam.projection == "Cylindrical":
            projection = ct.CylindricalProjection(
                focallength_mm=self.cam.focallength_mm,
                image_width_px=self.cam.img_w_px,
                image_height_px=self.cam.img_h_px,
                sensor_width_mm=self.cam.sensor_w_mm,
                sensor_height_mm=self.cam.sensor_h_mm,
                center_x_px=self.cam.center_x_px,
                center_y_px=self.cam.center_y_px)
        elif self.cam.projection == "Equirectangular":
            projection = ct.EquirectangularProjection(
                focallength_mm=self.cam.focallength_mm,
                image_width_px=self.cam.img_w_px,
                image_height_px=self.cam.img_h_px,
                sensor_width_mm=self.cam.sensor_w_mm,
                sensor_height_mm=self.cam.sensor_h_mm,
                center_x_px=self.cam.center_x_px,
                center_y_px=self.cam.center_y_px)

        else:  # default to rectilinear projection
            print("Defaulting to rectiliniear")
            projection = ct.RectilinearProjection(
                focallength_mm=self.cam.focallength_mm,
                image_width_px=self.cam.img_w_px,
                image_height_px=self.cam.img_h_px,
                sensor_width_mm=self.cam.sensor_w_mm,
                sensor_height_mm=self.cam.sensor_h_mm,
                center_x_px=self.cam.center_x_px,
                center_y_px=self.cam.center_y_px)
        self.camera = ct.Camera(orientation=orientation, projection=projection)
        self.camera.setGPSpos(lat=self.cam.gps_lat,
                              lon=self.cam.gps_lon,
                              elevation=self.cam.elevation_m)

    def run(self, start_frame=0):

        self.frame = self.cp.getCurrentFrame()
        print("processing frame nr %d" % self.frame)

        self.updateCameraParameters()

        # # get image parameters
        # image = self.db.getImage(frame=start_frame)
        # data = image.data
        # im_height, im_width, channels = data.shape

        # self.camera = ct.Camera(self.cam.)
        if self.cbFitParameters.isChecked():
            # try to load marker
            horizon = self.db.getMarkers(type="DM_horizon", frame=self.frame)

            if horizon.count() < 2:
                print(
                    "ERROR: Too few horizon markers placed - please add at least TWO horizon markers"
                )
                return

            self.camera.fixHorizon(horizon)
            if self.position.cam_elevation:
                self.camera.fixHeight(self.position.cam_elevation)

            # set fit parameter
            self.fit_params = dotdict()
            self.fit_params.horizon_points = horizon.count()
            self.fit_params.cam_elevation = self.camera.height
            self.fit_params.cam_tilt = np.round(self.camera.tilt, 2)
            self.fit_params.dist_to_horizon = np.round(
                self.camera.distanceToHorizon(), 2)

            # update params
            self.leCamTilt.setText(getNumber(self.camera.tilt, "%.2f"))
            self.leCamPan.setText(getNumber(self.camera.heading, "%.2f"))
            self.leCamRoll.setText(getNumber(self.camera.roll, "%.2f"))

        # succesfully initialized
        self.initialized = True

        # DEBUG
        # print("Fit Parameter:")
        # print(json.dumps(self.fit_params, indent=4, sort_keys=True))

        # TODO: add region update?
        self.updateAllMarker()

        # mask handling
        if self.cbCalcArea.isChecked():
            q_mask = self.db.getMasks(frame=self.frame)
            print("masks: ", q_mask.count())

            rotation = self.db.getOption('rotation')
            print("rotation ", rotation)

            print(self.camera)

            if q_mask.count() > 0:
                # get mask data
                mask = q_mask[0].data

                # binaryse
                mask[mask > 0] = 1

                mask_labeled = label(mask)
                props = regionprops(mask_labeled)

                # handle rotated images
                if rotation == 180:
                    self.LUT = self.camera.generateLUT(invert=True)
                else:
                    self.LUT = self.camera.generateLUT()

                # calculate corrected area
                for idx, n in enumerate(
                        np.unique(mask_labeled)[1:]):  # we exclude 0
                    area = np.zeros(mask_labeled.shape)
                    area[mask_labeled == n] = 1

                    corrected_area = np.sum(np.sum(area, axis=1) * self.LUT)

                    props[idx].corrected_area = corrected_area

                self.db.deleteMarkers(type='DM_area', image=q_mask[0].image)

                # iterate over properties and set marker for display
                for nr, prop in enumerate(props):

                    self.db.setMarker(image=q_mask[0].image,
                                      x=prop.centroid[1],
                                      y=prop.centroid[0],
                                      text=u'%.2f px²\n%.2f m²' %
                                      (prop.area, prop.corrected_area),
                                      type='DM_area')

            self.cp.reloadMarker(frame=self.frame)

        # show the horizon line
        self.plotHorizon()

    def plotHorizon(self):

        if self.cbShowHorizon.isChecked():
            # delete old horizon
            self.deleteHorizon()

            # get coordinates
            p1 = self.camera.getImageHorizon()[0]
            p2 = self.camera.getImageHorizon()[-1]
            print(p1, p2)

            # set pen
            pen = QtGui.QPen(QtGui.QColor("#00ffff"))
            pen.setWidth(5)
            pen.setCosmetic(True)

            # add object
            self.horizon_line = QtWidgets.QGraphicsLineItem(
                QtCore.QLineF(QtCore.QPointF(*p1), QtCore.QPointF(*p2)))
            self.horizon_line.setPen(pen)
            self.horizon_line.setParentItem(self.cp.window.view.origin)
            self.horizon_line.setZValue(100)

        else:
            self.deleteHorizon()

    def deleteHorizon(self):
        if self.horizon_line:
            print("delete")
            self.horizon_line.scene().removeItem(self.horizon_line)
            self.horizon_line = None

    def update(self):
        self.updateCameraParameters()
        self.updateAllMarker()

    def updateAllMarker(self):

        # get distance to cam markers and calculate distance
        dist2cam = self.db.getMarkers(type="DM_to_cam", frame=self.frame)
        for marker in dist2cam:
            self.updateDistMarker(marker)

        # get line markers and calculate distance in between
        dist2pt = self.db.getLines(type="DM_between", frame=self.frame)
        for line in dist2pt:
            self.updateDistLine(line)

        # get scalebox markers and update size
        distscalebox = self.db.getMarkers(type="DM_scalebox", frame=self.frame)
        for marker in distscalebox:
            self.updateScalebox(marker)

        # get scalebox markers and update size
        gpsMarkers = self.db.getMarkers(type="DM_GPS", frame=self.frame)
        for marker in gpsMarkers:
            self.updateGPS(marker)
        self.cp.reloadMarker(frame=self.frame)

        self.plotHorizon()

        self.cp.reloadMarker(frame=self.frame)

    def updateDistMarker(self, marker):
        """
        Update distance to observer indicated by marker
        """
        pos = self.camera.spaceFromImage(np.array([marker.x, marker.y]), Z=0).T

        marker.text = "%.2fm" % np.sqrt(pos[0]**2 + pos[1]**2)
        marker.save()

    def updateDistLine(self, line):
        """
        Update distance indicated by an line marker
        """
        pts = np.round(np.array([line.getPos1(), line.getPos2()]), 0)

        #TODO: Why doesnt this work when i supply the same point twice in an array?
        pos1 = self.camera.spaceFromImage(pts[0],
                                          Z=self.position.plane_elevation).T
        pos2 = self.camera.spaceFromImage(pts[1],
                                          Z=self.position.plane_elevation).T
        dist = np.sqrt(np.sum(((pos1 - pos2)**2)))

        line.text = "%.2fm" % dist
        line.save()

    def updateScalebox(self, marker):
        scalebox_dim = self.sbScaleboxDim.value()
        if marker.id in self.scalebox_dict:
            sb = self.scalebox_dict[marker.id]
            sb.delete()
            self.scalebox_dict.pop(marker.id)
            if marker.text is not None:
                match = re.match(r"^(?P<dim>\d*\.?\d*)\s*(?P<unit>\S*)?.*",
                                 marker.text)
                if match:
                    match = match.groupdict()
                    if match["unit"] in UNITS.keys():
                        scalebox_dim = float(
                            match["dim"]) * UNITS[match["unit"]]
                    else:
                        scalebox_dim = float(match["dim"])

        sb = ScaleBox(self.cp.window.view.origin, '#ff5f00',
                      np.array([marker.x, marker.y]), self.camera,
                      scalebox_dim)
        self.scalebox_dict[marker.id] = sb

    def updateGPS(self, marker):
        locationData = json.loads(marker.style)
        if "lon" in locationData and "lat" in locationData:
            lon = locationData["lon"]
            lat = locationData["lat"]
            if "h" in locationData:
                h = locationData["h"]
            else:
                h = 0
            imagePos = self.camera.imageFromSpace(self.camera.spaceFromGPS(
                np.array([lat, lon, h])),
                                                  hide_backpoints=False)
            if np.all(~np.isnan(imagePos)):
                marker.x = imagePos[0]
                marker.y = imagePos[1]
                marker.save()
            else:
                print("gps position of marker is not in Image!")
        # pass

    def markerMoveEvent(self, marker):
        """
        On moving a marker - update the text information
        """
        if self.initialized:
            if marker.type.name == 'DM_to_cam':
                self.updateDistMarker(marker)

            if marker.type.name == 'DM_between':
                self.updateDistLine(marker)

            if marker.type.name == 'DM_scalebox':
                self.updateScalebox(marker)

    def markerRemoveEvent(self, marker):
        if self.initialized:
            if marker.type.name == 'DM_scalebox':
                self.scalebox_dict[marker.id].delete()
                del self.scalebox_dict[marker.id]

    def markerAddEvent(self, marker):
        """
        On adding a marker - calculate values
        """
        if self.initialized:
            if marker.type.name == 'DM_to_cam':
                self.updateDistMarker(marker)

            if marker.type.name == 'DM_between':
                self.updateDistLine(marker)

            if marker.type.name == 'DM_scalebox':
                self.updateScalebox(marker)

    def pushbutton_refreshprojction(self):
        print("Refresh projection")

        # frame = self.cp.getCurrentFrame()
        img = self.cp.getImage().data
        if not isinstance(img, np.ndarray):
            img = img.image

        rotation = self.db.getOption('rotation')
        print('rotation', rotation)
        if rotation == 180:
            img = img[::-1, ::-1, :]

        extent = self.leExtent.text()
        scaling = getFloat(self.leScaling.text())

        print(extent)
        print(scaling)

        try:
            ex1, ex2, ex3, ex4 = extent.split(',')
            print("Setting custom extent to:", ex1, ex2, ex3, ex4)
            top_view = self.camera.getTopViewOfImage(
                img,
                extent=np.array([ex1, ex2, ex3, ex4]).astype(float),
                scaling=scaling)
        except:
            top_view = self.camera.getTopViewOfImage(img, scaling=scaling)

        self.pltWidget.axes.imshow(top_view)
        self.pltWidget.show()

    def buttonPressedEvent(self):
        # show the addon window when the button in ClickPoints is pressed
        self.show()

        try:
            self.run()
        except:
            pass

    def delete(self):
        # clean up on reload
        self.deleteHorizon()

        # clean scale boxes
        for i, sb in self.scalebox_dict.items():
            sb.delete()
Example #6
0
class Addon(clickpoints.Addon):
    def __init__(self, *args, **kwargs):
        clickpoints.Addon.__init__(self, *args, **kwargs)
        # set the title and layout
        self.setWindowTitle("Excel Exporter - ClickPoints")
        layout = QtWidgets.QVBoxLayout(self)

        # add a file chooser for the output
        self.line_edit_file = AddQSaveFileChoose(layout, "Path", value=self.db._database_filename.replace('.cdb', '.xls'), file_type="Excel Workbook (*.xls)")
        # add a mode selector, which formatting should be used for the output
        self.combo_style = AddQComboBox(layout, "Mode", values=["Marker Count", "Marker Positions", "Track Positions"])
        self.button_run = QtWidgets.QPushButton("Export")
        self.button_run.clicked.connect(self.run)
        layout.addWidget(self.button_run)

    def buttonPressedEvent(self):
        self.show()

    def exportMarkerCount(self, wb):
        wb_sheet = wb.add_sheet('data')

        # get types and images
        q_types = self.db.getMarkerTypes()
        q_images = self.db.getImages()

        # write xls header
        wb_sheet.write(0, 0, "sort_idx")
        wb_sheet.write(0, 1, "filename")
        for idx, type in enumerate(q_types):
            wb_sheet.write(0, idx + 2, type.name + '_count')
        header_offset = 1

        # write marker counts per image
        for ridx, image in enumerate(q_images):
            ridx += header_offset
            wb_sheet.write(ridx, 0, image.sort_index)
            wb_sheet.write(ridx, 1, image.filename)

            # extract type information
            for idx, type in enumerate(q_types):
                if type.mode == 1:
                    q_marker = self.db.getRectangles(type=type, image=image)
                elif type.mode == 2:
                    q_marker = self.db.getLines(type=type, image=image)
                else:
                    q_marker = self.db.getMarkers(type=type, image=image)

                wb_sheet.write(ridx, idx + 2, q_marker.count())

    def exportMarkerPositions(self, wb):

        # get types and images
        q_types = self.db.getMarkerTypes()
        q_images = self.db.getImages()

        for type in q_types:
            wb_sheet = wb.add_sheet(type.name)

            # write xls header
            wb_sheet.write(0, 0, "sort_idx")
            wb_sheet.write(0, 1, "filename")
            header_offset = 1
            maximum_header_column = -1

            # write marker counts per image
            for ridx, image in enumerate(q_images):
                ridx += header_offset
                wb_sheet.write(ridx, 0, image.sort_index)
                wb_sheet.write(ridx, 1, image.filename)

                # extract type information for markers
                try:
                    marker_query = self.db.getMarkers(image=image, type=type)
                except ValueError:
                    # not a marker type
                    continue
                for idx, marker in enumerate(marker_query):
                    wb_sheet.write(ridx, idx*2 + 2, marker.x)
                    wb_sheet.write(ridx, idx*2 + 3, marker.y)

                    # add headers x, y
                    if idx > maximum_header_column:
                        wb_sheet.write(0, idx * 2 + 2, "x")
                        wb_sheet.write(0, idx * 2 + 3, "y")
                        maximum_header_column = idx

                # extract type information for lines
                try:
                    line_query = self.db.getLines(image=image, type=type)
                except ValueError:
                    # type is not a line type
                    continue
                for idx, marker in enumerate(line_query):
                    wb_sheet.write(ridx, idx * 4 + 2, marker.x1)
                    wb_sheet.write(ridx, idx * 4 + 3, marker.y1)
                    wb_sheet.write(ridx, idx * 4 + 4, marker.x2)
                    wb_sheet.write(ridx, idx * 4 + 5, marker.y2)

                    # add headers x1, y1, x2, y2
                    if idx > maximum_header_column:
                        wb_sheet.write(0, idx * 4 + 2, "x1")
                        wb_sheet.write(0, idx * 4 + 3, "y1")
                        wb_sheet.write(0, idx * 4 + 4, "x2")
                        wb_sheet.write(0, idx * 4 + 5, "y2")
                        maximum_header_column = idx

                # extract type information for rectangles
                for idx, marker in enumerate(self.db.getRectangles(image=image, type=type)):
                    wb_sheet.write(ridx, idx * 4 + 2, marker.x)
                    wb_sheet.write(ridx, idx * 4 + 3, marker.y)
                    wb_sheet.write(ridx, idx * 4 + 4, marker.width)
                    wb_sheet.write(ridx, idx * 4 + 5, marker.height)

                    # add headers x, y, width, height
                    if idx > maximum_header_column:
                        wb_sheet.write(0, idx * 4 + 2, "x")
                        wb_sheet.write(0, idx * 4 + 3, "y")
                        wb_sheet.write(0, idx * 4 + 4, "width")
                        wb_sheet.write(0, idx * 4 + 5, "height")
                        maximum_header_column = idx

    def exportTrackPositions(self, wb):
        wb_sheet = wb.add_sheet('data')

        # get types and images
        q_types = self.db.getMarkerTypes()
        q_images = self.db.getImages()

        # write xls header
        wb_sheet.write(0, 0, "sort_idx")
        wb_sheet.write(0, 1, "filename")
        # get the tracks for each type
        tracks = []
        for idx, type in enumerate(q_types):
            try:
                track_query = self.db.getTracks(type=type)
            except ValueError:
                # ignore marker types that are not tracks
                continue
            for track in track_query:
                wb_sheet.write(0, len(tracks)*2 + 2, type.name + ' #%d' % track.id)
                tracks.append(track)
        header_offset = 1

        # write marker counts per image
        for ridx, image in enumerate(q_images):
            ridx += header_offset
            wb_sheet.write(ridx, 0, image.sort_index)
            wb_sheet.write(ridx, 1, image.filename)

            # write the marker position for each track
            for idx, track in enumerate(tracks):
                marker = self.db.getMarkers(image=image, track=track)
                if marker.count():
                    x, y = marker[0].pos()
                    wb_sheet.write(ridx, idx*2 + 2, x)
                    wb_sheet.write(ridx, idx*2 + 3, y)

    def run(self, start_frame=0):
        # prepare write to excel
        wb_name = self.line_edit_file.text()
        wb = xlwt.Workbook()

        # List Marker Count
        if self.combo_style.currentIndex() == 0:
            self.exportMarkerCount(wb)
        # List Marker Positions
        if self.combo_style.currentIndex() == 1:
            self.exportMarkerPositions(wb)
        # List Track Positions
        if self.combo_style.currentIndex() == 2:
            self.exportTrackPositions(wb)

        print("Writing to %s" % wb_name)
        try:
            wb.save(wb_name)
        except PermissionError as err:
            QtWidgets.QMessageBox.critical(self, 'Error - ClickPoints',
                                           '%s\n\nMaybe the file is still open in Excel. Please close it and try again.' % err,
                                           QtWidgets.QMessageBox.Ok)
            raise err
        QtWidgets.QMessageBox.information(self, 'Add-on - ClickPoints',
                                          'Data saved to %s.' % wb_name, QtWidgets.QMessageBox.Ok)
        print("DONE")