def save_track(dataset, track, folder, labels_dir, ext="png"):
    track_data = dataset.fetch_track(track, original=True)
    clip_meta = dataset.db.get_clip_meta(track.clip_id)

    clip_dir = os.path.join(folder, str(track.clip_id))
    if not os.path.isdir(clip_dir):
        logging.debug("Creating %s", clip_dir)
        os.mkdir(clip_dir)

    background = dataset.db.get_clip_background(track.clip_id)
    if background is not None:
        background, _ = normalize(background, new_max=255)

        img = Image.fromarray(np.uint8(background))
        filename = "{}-background.{}".format(track.clip_id, ext)
        logging.debug("Saving %s", os.path.join(clip_dir, filename))
        img.save(os.path.join(clip_dir, filename))

    for i, frame in enumerate(track_data):
        thermal = frame.thermal
        normed, _ = normalize(thermal, new_max=255)
        img = Image.fromarray(np.uint8(normed))
        filename = "{}-{}.{}".format(track.clip_id, i + track.start_frame, ext)
        logging.debug("Saving %s", os.path.join(clip_dir, filename))

        img.save(os.path.join(clip_dir, filename))
    save_metadata(clip_meta, track, labels_dir)
def preprocess_frame(
    frame,
    frame_size,
    augment,
    thermal_median,
    velocity,
    output_dim,
    preprocess_fn=None,
    sample=None,
):
    processed_frame, flipped = preprocess_segment(
        [frame],
        frame_size,
        reference_level=[thermal_median],
        augment=augment,
        default_inset=0,
    )
    if len(processed_frame) == 0:
        return
    processed_frame = processed_frame[0]
    thermal = processed_frame.get_channel(TrackChannels.thermal)
    filtered = processed_frame.get_channel(TrackChannels.filtered)
    thermal, stats = imageprocessing.normalize(thermal, min=0)
    if not stats[0]:
        return None
    filtered, stats = imageprocessing.normalize(filtered, min=0)
    if not stats[0]:
        return None

    data = np.empty((*thermal.shape, 3))
    data[:, :, 0] = thermal
    data[:, :, 1] = filtered
    data[:, :, 2] = filtered
    # for testing
    # tools.saveclassify_image(
    #     data,
    #     f"samples/{sample.label}-{sample.clip_id}-{sample.track_id}",
    # )

    # preprocess expects values in range 0-255
    if preprocess_fn:
        data = data * 255
        data = preprocess_fn(data)
    return data
def preprocess_movement(
    data,
    segment,
    frames_per_row,
    regions,
    channel,
    preprocess_fn=None,
    augment=False,
):
    segment = preprocess_segment(segment,
                                 augment=augment,
                                 filter_to_delta=False,
                                 default_inset=0)

    segment = segment[:, channel]

    # as long as one frame it's fine
    square, success = imageprocessing.square_clip(segment, frames_per_row,
                                                  (FRAME_SIZE, FRAME_SIZE),
                                                  type)
    if not success:
        return None
    dots, overlay = imageprocessing.movement_images(
        data,
        regions,
        dim=square.shape,
        channel=channel,
        require_movement=True,
    )
    dots = dots / 255
    overlay, success = imageprocessing.normalize(overlay, min=0)
    if not success:
        return None

    data = np.empty((square.shape[0], square.shape[1], 3))
    data[:, :, 0] = square
    data[:, :, 1] = dots  # dots
    data[:, :, 2] = overlay  # overlay

    if preprocess_fn:
        for i, frame in enumerate(data):
            frame = frame * 255
            data[i] = preprocess_fn(frame)
    return data
Example #4
0
    def _get_filtered_frame(self, clip, thermal):
        """
        Calculates filtered frame from thermal
        :param thermal: the thermal frame
        :param background: (optional) used for background subtraction
        :return: uint8 filtered frame and adjusted clip threshold for normalized frame
        """

        filtered = np.float32(thermal.copy())
        avg_change = int(round(np.average(thermal) - clip.stats.mean_background_value))
        np.clip(filtered - clip.background - avg_change, 0, None, out=filtered)

        filtered, stats = normalize(filtered, new_max=255)
        filtered = cv2.fastNlMeansDenoising(np.uint8(filtered), None)
        if stats[1] == stats[2]:
            mapped_thresh = clip.background_thresh
        else:
            mapped_thresh = clip.background_thresh / (stats[1] - stats[2]) * 255
        return filtered, mapped_thresh
    def create_four_tracking_image(self, frame, min_temp, max_temp):

        thermal = frame.thermal
        filtered = frame.filtered + min_temp
        filtered = tools.convert_heat_to_img(filtered, self.colourmap,
                                             min_temp, max_temp)
        thermal = tools.convert_heat_to_img(thermal, self.colourmap, min_temp,
                                            max_temp)
        if self.debug:
            tools.add_heat_number(thermal, frame.thermal, 1)
        if frame.mask is None:
            mask = np.zeros((np.array(thermal).shape), dtype=np.uint8)
        else:
            mask, _ = normalize(frame.mask, new_max=255)
            mask = np.uint8(mask)

            mask = mask[..., np.newaxis]
            mask = np.repeat(mask, 3, axis=2)

        mask = Image.fromarray(mask)
        flow_h, flow_v = frame.get_flow_split(clip_flow=True)
        if flow_h is None and flow_v is None:
            flow_magnitude = Image.fromarray(
                np.zeros((np.array(thermal).shape), dtype=np.uint8))
        else:
            flow_magnitude = (
                np.linalg.norm(np.float32([flow_h, flow_v]), ord=2, axis=0) /
                4.0 + min_temp)
            flow_magnitude = tools.convert_heat_to_img(flow_magnitude,
                                                       self.colourmap,
                                                       min_temp, max_temp)
        image = np.hstack((np.vstack(
            (thermal, mask)), np.vstack((filtered, flow_magnitude))))
        image = Image.fromarray(image)

        image = image.resize(
            (
                int(image.width * 4),
                int(image.height * 4),
            ),
            Image.BILINEAR,
        )
        return image
Example #6
0
def preprocess_frame(
    data, output_dim, use_thermal=True, augment=False, preprocess_fn=None
):
    if use_thermal:
        channel = TrackChannels.thermal
    else:
        channel = TrackChannels.filtered
    data = data[channel]
    data, stats = imageprocessing.normalize(data)
    if not stats[0]:
        return None

    data = data[np.newaxis, :]
    data = np.transpose(data, (1, 2, 0))
    data = np.repeat(data, output_dim[2], axis=2)
    data = imageprocessing.resize_cv(data, output_dim, channel)

    # preprocess expects values in range 0-255
    if preprocess_fn:
        data = data * 255
        data = preprocess_fn(data)
    return data
Example #7
0
    def generate_optical_flow(self, opt_flow, prev_frame, flow_threshold=40):
        """
        Generate optical flow from thermal frames
        :param opt_flow: An optical flow algorithm
        """
        height, width = self.thermal.shape
        flow = np.zeros([height, width, 2], dtype=np.float32)
        scaled_thermal = self.thermal.copy()
        scaled_thermal[self.mask == 0] = 0
        scaled_thermal, _ = normalize(scaled_thermal, new_max=255)
        scaled_thermal = np.float32(scaled_thermal)

        # threshold = np.median(self.thermal) + flow_threshold
        # scaled_thermal = np.uint8(np.clip(self.thermal - threshold, 0, 255))
        if prev_frame is not None:
            # for some reason openCV spins up lots of threads for this which really slows things down, so we
            # cap the threads to 2
            cv2.setNumThreads(2)
            flow = opt_flow.calc(prev_frame.scaled_thermal, scaled_thermal,
                                 flow)
        self.scaled_thermal = scaled_thermal
        self.flow = flow
        if prev_frame:
            prev_frame.scaled_thermal = None
Example #8
0
def preprocess_movement(
    data,
    segment,
    frames_per_row,
    frame_size,
    regions,
    red_channel,
    preprocess_fn=None,
    augment=False,
    green_type=None,
    keep_aspect=False,
    reference_level=None,
):
    segment = preprocess_segment(
        segment,
        reference_level=reference_level,
        augment=augment,
        filter_to_delta=False,
        default_inset=0,
        keep_aspect=keep_aspect,
        frame_size=frame_size,
    )

    red_segment = segment[:, red_channel]
    # as long as one frame it's fine
    red_square, success = imageprocessing.square_clip(
        red_segment, frames_per_row, (frame_size, frame_size), type
    )
    if not success:
        return None
    _, overlay = imageprocessing.movement_images(
        data,
        regions,
        dim=red_square.shape,
        require_movement=True,
    )
    overlay, stats = imageprocessing.normalize(overlay, min=0)
    if not stats[0]:
        return None

    data = np.empty((red_square.shape[0], red_square.shape[1], 3))
    data[:, :, 0] = red_square
    if green_type == FrameTypes.filtered_square:
        green_segment = segment[:, TrackChannels.filtered]
        green_square, success = imageprocessing.square_clip(
            green_segment, frames_per_row, (frame_size, frame_size), type
        )

        if not success:
            return None
    elif green_type == FrameTypes.thermal_square:
        green_segment = segment[:, TrackChannels.thermal]
        green_square, success = imageprocessing.square_clip(
            green_segment, frames_per_row, (frame_size, frame_size), type
        )
        if not success:
            return None
    elif green_type == FrameTypes.overlay:
        green_square = overlay
    else:
        green_square = np.zeros(overlay.shape)

    data[:, :, 1] = green_square
    data[:, :, 2] = overlay  # overlay

    if preprocess_fn:
        data = data * 255
        data = preprocess_fn(data)
    return data
Example #9
0
 def normalize(self):
     if self.thermal is not None:
         self.thermal, _ = normalize(self.thermal, new_max=255)
     if self.filtered is not None:
         self.filtered, _ = normalize(self.filtered, new_max=255)
def preprocess_movement(
    segment,
    frames_per_row,
    frame_size,
    red_type,
    green_type,
    blue_type,
    preprocess_fn=None,
    augment=False,
    reference_level=None,
    sample=None,
    keep_edge=False,
):
    segment, flipped = preprocess_segment(
        segment,
        frame_size,
        reference_level=reference_level,
        augment=augment,
        default_inset=0,
        keep_edge=keep_edge,
    )
    frame_types = {}
    channel_types = set([green_type, blue_type, red_type])
    for type in channel_types:
        if type == FrameTypes.overlay:
            if overlay is None:
                overlay = imageprocessing.overlay_image(
                    data,
                    regions,
                    dim=(frames_per_row * frame_size,
                         frames_per_row * frame_size),
                    require_movement=True,
                )
                channel_data, stats = imageprocessing.normalize(overlay, min=0)
                if not stats[0]:
                    return None
            else:
                channel_data = np.zeros((square.shape[0], square.shape[1]))
                channel_data[:overlay.shape[0], :overlay.shape[1]] = overlay

            if flipped:
                channel_data = np.flip(channel_data, axis=1)
        elif type == FrameTypes.flow_tiled:
            channel_segment = [
                frame.get_channel(TrackChannels.flow) for frame in segment
            ]
            channel_data, success = imageprocessing.square_clip_flow(
                channel_segment, frames_per_row, (frame_size, frame_size))
            if not success:
                return None
        else:
            if type == FrameTypes.thermal_tiled:
                channel = TrackChannels.thermal
            else:
                channel = TrackChannels.filtered

            channel_segment = [frame.get_channel(channel) for frame in segment]
            channel_data, success = imageprocessing.square_clip(
                channel_segment, frames_per_row, (frame_size, frame_size))

            if not success:
                return None

        frame_types[type] = channel_data

    data = np.stack((frame_types[red_type], frame_types[green_type],
                     frame_types[blue_type]),
                    axis=2)
    # for testing
    # tools.saveclassify_image(
    #     data,
    #     f"samples/{sample}",
    # )
    if preprocess_fn:
        data = data * 255
        data = preprocess_fn(data)
    return data
Example #11
0
    def remove_background_animals(self, initial_frame, initial_diff):
        """
        Try and remove animals that are already in the initial frames, by
        checking for connected components in the intital_diff frame
        (this is the maximum change between first frame and all other frames in the clip)
        """
        # remove some noise
        initial_diff[initial_diff < self.background_thresh] = 0
        initial_diff[initial_diff > 255] = 255
        initial_diff = np.uint8(initial_diff)
        initial_diff = cv2.fastNlMeansDenoising(initial_diff, None)

        _, lower_mask, lower_objects = detect_objects(initial_diff, otsus=True)

        max_region = Region(0, 0, self.res_x, self.res_y)
        for component in lower_objects[1:]:
            region = Region(component[0], component[1], component[2],
                            component[3])
            region.enlarge(2, max=max_region)
            if region.width >= self.res_x or region.height >= self.res_y:
                logging.info(
                    "Background animal bigger than max, probably false positive %s %s",
                    region,
                    component[4],
                )
                continue
            background_region = region.subimage(initial_frame)
            norm_back = background_region.copy()
            norm_back, _ = normalize(norm_back, new_max=255)
            sub_components, sub_connected, sub_stats = detect_objects(
                norm_back, otsus=True)

            if sub_components <= 1:
                continue
            overlap_image = region.subimage(lower_mask) * 255
            overlap_pixels = np.sum(sub_connected[overlap_image > 0])
            overlap_pixels = overlap_pixels / float(component[4])

            # filter out components which are too big, or dont match original causes
            # for filtering
            if (overlap_pixels < Clip.MIN_ORIGIN_OVERLAP
                    or sub_stats[1][4] == 0 or sub_stats[1][4] == region.area):
                logging.info(
                    "Invalid components mass: %s, components: %s region area %s overlap %s",
                    sub_stats[1][4],
                    sub_components,
                    region.area,
                    overlap_pixels,
                )
                continue

            sub_connected[sub_connected > 0] = 1
            # remove this component from the background by painting with
            # colours of neighbouring pixels
            background_region[:] = cv2.inpaint(
                np.float32(background_region),
                np.uint8(sub_connected),
                3,
                cv2.INPAINT_TELEA,
            )
        return initial_frame