Exemplo n.º 1
0
    def LoadingFinishedEvent(self):
        if self.config.server_annotations:
            import peewee
            self.server = peewee.MySQLDatabase(self.config.sql_dbname,
                                               host=self.config.sql_host,
                                               port=self.config.sql_port,
                                               user=self.config.sql_user,
                                               passwd=self.config.sql_pwd)
            self.db = AnnotationFile(self.data_file, self.server)

            # place tick marks for already present masks
            for item in self.db.get_annotation_frames():
                try:
                    image = self.data_file.table_image.get(
                        external_id=item.file_id, frame=item.frame)
                    BroadCastEvent(self.modules, "AnnotationMarkerAdd",
                                   image.sort_index)
                    self.annoation_ids.append(item.id)
                except peewee.DoesNotExist:
                    pass

        else:
            self.data_file = self.data_file
            self.db = AnnotationFile(self.data_file)

            # place tick marks for already present masks
            for item in self.db.get_annotation_frames():
                BroadCastEvent(self.modules, "AnnotationMarkerAdd",
                               item.image.sort_index)
                self.annoation_ids.append(item.id)
Exemplo n.º 2
0
    def DrawLine(self, start_x: float, end_x: float, start_y: float,
                 end_y: float) -> None:
        # draw the line on the mask
        if self.scene_parent.tool_index == 0:
            self.parent.MaskDisplay.DrawLine(
                start_x, end_x, start_y, end_y, self.parent.DrawCursorSize,
                self.parent.maskTypeChooser.active_draw_type)
        else:
            self.parent.MaskDisplay.DrawLine(start_x, end_x, start_y, end_y,
                                             self.parent.DrawCursorSize, 0)
        self.parent.MaskChanged = True
        self.parent.MaskUnsaved = True

        # directly update the mask or display last stroke as drawPath
        if self.parent.config.auto_mask_update:
            self.parent.RedrawMask()
        else:
            self.parent.drawPath.moveTo(start_x, start_y)
            self.parent.drawPath.lineTo(end_x, end_y)
            self.parent.drawPathItem.setPath(self.parent.drawPath)

        # if the mask was empty notify modules that a new mask was created
        if self.parent.MaskEmpty:
            self.parent.MaskEmpty = False
            BroadCastEvent(self.parent.modules, "MaskAdded")
Exemplo n.º 3
0
    def updateDataFile(self, data_file: DataFileExtended, new_database: bool) -> None:
        self.data_file = data_file
        self.config = data_file.getOptionAccess()

        self.mask_file = MaskFile(data_file)

        # if a new database is created take mask types from config
        if new_database:
            for type_id, type_def in enumerate(self.config.draw_types):
                if len(type_def) >= 3:
                    name = type_def[2]
                else:
                    name = "Color%d" % type_id
                self.mask_file.set_type(type_id, name, type_def[1], type_def[0])

        # update mask interface buttons
        self.maskTypeChooser.updateButtons(self.mask_file)

        # get config options
        self.changeOpacity(self.config.mask_opacity - self.mask_opacity)
        self.changeCursorSize(self.config.mask_brush_size - self.DrawCursorSize)
        self.ToggleInterfaceEvent(hidden=self.config.mask_interface_hidden)
        if self.config.selected_draw_type >= 0:
            self.maskTypeChooser.setActiveDrawType(self.config.selected_draw_type)

        # place tick marks for already present masks
        # but lets take care that there are masks ...
        try:
            frames = np.array(self.mask_file.get_mask_frames().tuples())[:, 0]
            BroadCastEvent(self.modules, "MarkerPointsAddedList", frames)
        except IndexError:
            pass
Exemplo n.º 4
0
 def saveAnnotation(self):
     # save the annotation
     try:
         self.db.annotation.save()
     # readonly mode
     except peewee.OperationalError:
         QtWidgets.QMessageBox.critical(
             self, 'Error - ClickPoints',
             'Database is opened in read-only mode.',
             QtWidgets.QMessageBox.Ok)
         return
     # update tag association table
     self.db.setTags(self.leTag.getTagList())
     self.db.annotation.tags = ",".join(self.leTag.getTagList())
     self.close()
     BroadCastEvent(self.modules, "AnnotationAdded", self.db.annotation)
     # set the database changed flag
     self.db.data_file.setChangesMade()
Exemplo n.º 5
0
 def do_redo(self) -> None:
     if self.undo.get_state()[1] is not None:
         self.undo.redo()
         BroadCastEvent(self.modules, "imageLoadedEvent", "", 0)
Exemplo n.º 6
0
 def removeAnnotation(self):
     self.annotation_handler.db.remove_annotation(self.annotation)
     BroadCastEvent(self.modules, "AnnotationRemoved", self.db.annotation)
     self.close()
Exemplo n.º 7
0
    async def SaveImage_async(self) -> None:
        # hide the start button and display the abort button
        self.abort = False
        self.button_start.setHidden(True)
        self.button_stop.setHidden(False)

        # save options
        options = self.options
        options.export_video_filename = MakePathRelative(str(self.leAName.value()))
        options.export_image_filename = MakePathRelative(str(self.leANameI.value()))
        options.export_gif_filename = MakePathRelative(str(self.leANameG.value()))
        options.export_single_image_filename = MakePathRelative(str(self.leANameIS.value()))
        options.export_type = self.cbType.currentIndex()
        options.video_codec = str(self.leCodec.value())
        options.video_quality = self.sbQuality.value()
        options.export_display_time = self.cbTime.value()
        options.export_time_from_zero = self.cbTimeZero.value()
        options.export_time_font_size = self.cbTimeFontSize.value()
        options.export_time_font_color = self.cbTimeColor.value()
        options.export_image_scale = self.cbImageScaleSize.value()
        options.export_marker_scale = self.cbMarkerScaleSize.value()
        options.export_custom_time = self.cbCustomTime.value()
        options.export_custom_time_delta = self.cbCustomTimeDelta.value()
        options.export_time_format = self.cbTimeFormat.value()
        options.export_timedelta_format = self.cbTimeDeltaFormat.value()

        # get the marker handler for marker drawing
        marker_handler = self.window.GetModule("MarkerHandler")

        # get the timeline for start and end frames
        timeline = self.window.GetModule("Timeline")
        # extract start, end and skip
        start = timeline.frameSlider.startValue()
        end = timeline.frameSlider.endValue()
        skip = timeline.skip if timeline.skip >= 1 else 1

        # initialize writer object according to export mode
        writer = None
        svg = False
        if self.cbType.currentIndex() == 0:  # video
            path = str(self.leAName.value())
            writer_params = dict(format="avi", mode="I", fps=timeline.fps, codec=options.video_codec,
                                 quality=options.video_quality)
        elif self.cbType.currentIndex() == 1:  # image
            path = str(self.leANameI.value())
            if path.endswith(".svg"):
                svg = True
        elif self.cbType.currentIndex() == 2:  # gif
            path = str(self.leANameG.value())
            writer_params = dict(format="gif", mode="I", fps=timeline.fps)
        elif self.cbType.currentIndex() == 3:  # single image
            path = str(self.leANameIS.value())
            if path.endswith(".svg"):
                svg = True

        # create the output path if it doesn't exist
        if not os.path.exists(os.path.dirname(path)):
            try:
                os.mkdir(os.path.dirname(path))
            except OSError:
                print("ERROR: can't create folder %s", os.path.dirname(path))
                return
        # get timestamp draw parameter
        self.time_drawing = None
        if options.export_display_time:
            class TimeDrawing:
                pass

            self.time_drawing = TimeDrawing()
            try:
                self.time_drawing.font = ImageFont.truetype("arial.ttf", options.export_time_font_size)
            except IOError:
                self.time_drawing.font = ImageFont.truetype(
                    os.path.join(os.environ["CLICKPOINTS_ICON"], "FantasqueSansMono-Regular.ttf"),
                    self.cbTimeFontSize.value())
            self.time_drawing.start = None
            self.time_drawing.x = 15
            self.time_drawing.y = 10
            self.time_drawing.color = tuple(HTMLColorToRGB(options.export_time_font_color))
            if options.export_custom_time:
                self.custom_time = 0

        # initialize progress bar
        self.progressbar.setMinimum(start)
        self.progressbar.setMaximum(end)

        # determine export rect
        image = self.window.ImageDisplay.image
        offset = self.window.ImageDisplay.last_offset
        start_x, start_y, end_x, end_y = np.array(self.window.view.GetExtend(True)).astype("int") + np.hstack(
            (offset, offset)).astype("int")
        # constrain start points
        start_x = BoundBy(start_x, 0, image.shape[1])
        start_y = BoundBy(start_y, 0, image.shape[0])
        # constrain end points
        end_x = BoundBy(end_x, start_x + 1, image.shape[1])
        end_y = BoundBy(end_y, start_y + 1, image.shape[0])
        if (end_y - start_y) % 2 != 0: end_y -= 1
        if (end_x - start_x) % 2 != 0: end_x -= 1
        self.preview_slice = np.zeros((end_y - start_y, end_x - start_x, 3), self.window.ImageDisplay.image.dtype)

        # iterate over frames
        iter_range = range(start, end + 1, skip)
        if self.cbType.currentIndex() == 3:
            iter_range = [self.window.target_frame]
        for frame in iter_range:
            # advance progress bar and load next image
            self.progressbar.setValue(frame)
            await self.window.load_frame(frame)

            # get new image and offsets
            image = self.window.ImageDisplay.image
            offset = self.window.ImageDisplay.last_offset
            # split offsets in integer and decimal part
            offset_int = offset.astype("int")
            offset_float = offset - offset_int

            # calculate new slices
            start_x2 = start_x - offset_int[0]
            start_y2 = start_y - offset_int[1]
            end_x2 = end_x - offset_int[0]
            end_y2 = end_y - offset_int[1]
            # adapt new slices to fit in image
            start_x3 = BoundBy(start_x2, 0, image.shape[1])
            start_y3 = BoundBy(start_y2, 0, image.shape[0])
            end_x3 = BoundBy(end_x2, start_x3 + 1, image.shape[1])
            end_y3 = BoundBy(end_y2, start_y3 + 1, image.shape[0])

            # extract cropped image
            self.preview_slice = np.zeros((end_y - start_y, end_x - start_x, 3), self.window.ImageDisplay.image.dtype)
            self.preview_slice[start_y3 - start_y2:self.preview_slice.shape[0] + (end_y3 - end_y2),
            start_x3 - start_x2:self.preview_slice.shape[1] + (end_x3 - end_x2), :] = image[start_y3:end_y3,
                                                                                      start_x3:end_x3, :3]

            # draw mask on the image
            BroadCastEvent(self.window.modules, "drawToImage0", self.preview_slice, slice(start_y3, end_y3),
                           slice(start_x3, end_x3))

            # apply the subpixel decimal shift
            if offset_float[0] or offset_float[1]:
                self.preview_slice = shift(self.preview_slice, [offset_float[1], offset_float[0], 0])

            # use min/max & gamma correction
            if self.window.ImageDisplay.image_pixMapItem.conversion is not None:
                self.preview_slice = self.window.ImageDisplay.image_pixMapItem.conversion[self.preview_slice[:, :, :3]]

            # convert image to PIL draw object
            pil_image = Image.fromarray(self.preview_slice)
            if options.export_image_scale != 1:
                shape = np.array(
                    [self.preview_slice.shape[1], self.preview_slice.shape[0]]) * options.export_image_scale
                pil_image = pil_image.resize(shape.astype(int), Image.ANTIALIAS)
            draw = ImageDraw.Draw(pil_image)
            draw.pil_image = pil_image
            # init svg
            if svg:
                import svgwrite
                dwg = svgwrite.Drawing(path % (frame - start), profile='full',
                                       size=(self.preview_slice.shape[1], self.preview_slice.shape[0]))

            # draw marker on the image
            BroadCastEvent(self.window.modules, "drawToImage", draw, start_x - offset[0], start_y - offset[1],
                           options.export_marker_scale, options.export_image_scale, options.rotation)
            if svg:
                BroadCastEvent(self.window.modules, "drawToImageSvg", dwg, start_x - offset[0], start_y - offset[1],
                               options.export_marker_scale, options.export_image_scale, options.rotation)
            # rotate the image
            if self.data_file.getOption("rotation") != 0:
                angle = self.data_file.getOption("rotation")
                if angle == 90:
                    pil_image = pil_image.transpose(Image.ROTATE_270)
                elif angle == 180:
                    pil_image = pil_image.transpose(Image.ROTATE_180)
                elif angle == 270:
                    pil_image = pil_image.transpose(Image.ROTATE_90)
                else:
                    pil_image = pil_image.rotate(-angle)
                draw = ImageDraw.Draw(pil_image)
                draw.pil_image = pil_image
            # draw marker on the image
            BroadCastEvent(self.window.modules, "drawToImage2", draw, start_x - offset[0], start_y - offset[1],
                           options.export_marker_scale, options.export_image_scale, options.rotation)
            if svg:
                BroadCastEvent(self.window.modules, "drawToImage2Svg", dwg, start_x - offset[0], start_y - offset[1],
                               options.export_marker_scale, options.export_image_scale, options.rotation)
            # draw timestamp
            if self.time_drawing is not None:
                if options.export_custom_time:
                    text = formatTimedelta(datetime.timedelta(seconds=self.custom_time), options.export_timedelta_format)
                    draw.text((self.time_drawing.x, self.time_drawing.y), text, self.time_drawing.color,
                              font=self.time_drawing.font)
                    self.custom_time += options.export_custom_time_delta
                else:
                    time = self.window.data_file.image.timestamp
                    if time is not None:
                        if frame == start and options.export_time_from_zero:
                            self.time_drawing.start = time
                        if self.time_drawing.start is not None:
                            text = formatTimedelta(time - self.time_drawing.start, options.export_timedelta_format)
                        else:
                            text = time.strftime(options.export_time_format)
                        draw.text((self.time_drawing.x, self.time_drawing.y), text, self.time_drawing.color,
                                  font=self.time_drawing.font)
            # add to video ...
            if svg:
                dwg.save()
            else:
                if self.cbType.currentIndex() == 0:
                    if writer is None:
                        writer = imageio.get_writer(path, **writer_params)
                    writer.append_data(np.array(pil_image))
                # ... or save image ...
                elif self.cbType.currentIndex() == 1:
                    pil_image.save(path % (frame - start))
                elif self.cbType.currentIndex() == 3:
                    try:
                        pil_image.save(path % frame)
                    except TypeError:
                        pil_image.save(path)
                # ... or add to gif
                elif self.cbType.currentIndex() == 0 or self.cbType.currentIndex() == 2:
                    if writer is None:
                        writer = imageio.get_writer(path, **writer_params)
                    writer.append_data(np.array(pil_image))
            # process events so that the program doesn't stall
            self.window.app.processEvents()
            # abort if the user clicked the abort button
            if self.abort:
                break

        # set progress bar to the end and close output file
        self.progressbar.setValue(end)
        if self.cbType.currentIndex() == 2 or self.cbType.currentIndex() == 0:
            writer.close()

        # show the start button again
        self.button_start.setHidden(False)
        self.button_stop.setHidden(True)