Exemplo n.º 1
0
    def stack_frames(self):
        """
        Compute the shifted contributions of all frames to all alignment points and add them to the
        appropriate alignment point stacking buffers.

        :return: -
        """

        # First find out if there are holes between AP patches.
        self.prepare_for_stack_blending()

        # Initialize the array for shift distribution statistics.
        self.shift_distribution = full(
            (self.configuration.alignment_points_search_width * 2, ),
            0,
            dtype=np_int)
        self.shift_failure_counter = 0

        # If multi-level correlation AP matching is selected, prepare frame-independent data
        # structures used by this particular search algorithm.
        if self.configuration.alignment_points_method == 'MultiLevelCorrelation':
            # Set the two-level reference boxes for all APs.
            self.alignment_points.set_reference_boxes_correlation()

            # Compute the "weight matrix" used in the first correlation phase. It penalizes search
            # results far away from the center.
            search_width_second_phase = 4
            max_search_width_first_phase = int(
                (self.configuration.alignment_points_search_width -
                 search_width_second_phase) / 2)
            search_width_first_phase = max_search_width_first_phase
            extent = 2 * search_width_first_phase + 1
            weight_matrix_first_phase = empty((extent, extent), dtype=float32)
            for y in range(extent):
                for x in range(extent):
                    weight_matrix_first_phase[
                        y,
                        x] = 1. - self.configuration.alignment_points_penalty_factor * (
                            (y / search_width_first_phase - 1)**2 +
                            (x / search_width_first_phase - 1)**2)

        else:
            weight_matrix_first_phase = None

        # In debug mode: Prepare for de-warp visualization.
        if self.debug:
            self.create_image_window_signal.emit()

        # If brightness normalization is switched on, prepare for adjusting frame brightness.
        if self.configuration.frames_normalization:
            median_brightness = median(self.frames.frames_average_brightness)
            # print ("min: " + str(min(self.frames.frames_average_brightness)) + ", median: "
            #        + str(median_brightness) + ", max: "
            #        + str(max(self.frames.frames_average_brightness)))

        # Go through the list of all frames.
        for frame_index in range(self.frames.number):

            # If brightness normalization is switched on, change the brightness of this frame to
            # the median of all frames.
            if self.configuration.frames_normalization:
                frame = self.frames.frames(frame_index) * median_brightness / \
                        (self.frames.frames_average_brightness[frame_index] + 1.e-7)
            else:
                frame = self.frames.frames(frame_index)

            frame_mono_blurred = self.frames.frames_mono_blurred(frame_index)

            # After every "signal_step_size"th frame, send a progress signal to the main GUI.
            if self.progress_signal is not None and frame_index % self.signal_step_size == 1:
                self.progress_signal.emit(
                    "Stack frames",
                    int(round(10 * frame_index / self.frames.number) * 10))

            # Look up the constant shifts of the given frame with respect to the mean frame.
            dy = self.align_frames.dy[frame_index]
            dx = self.align_frames.dx[frame_index]

            # Go through all alignment points for which this frame was found to be among the best.
            for alignment_point_index in self.frames.used_alignment_points[
                    frame_index]:
                alignment_point = self.alignment_points.alignment_points[
                    alignment_point_index]

                # Compute the local warp shift for this frame.
                self.my_timer.start('Stacking: compute AP shifts')
                [
                    shift_y, shift_x
                ], success = self.alignment_points.compute_shift_alignment_point(
                    frame_mono_blurred,
                    frame_index,
                    alignment_point_index,
                    de_warp=self.configuration.alignment_points_de_warp,
                    weight_matrix_first_phase=weight_matrix_first_phase)

                # Increment the counter corresponding to the 2D warp shift.
                if success:
                    self.shift_distribution[int(
                        round(sqrt(shift_y**2 + shift_x**2)))] += 1
                else:
                    self.shift_failure_counter += 1

                # The total shift consists of three components: different coordinate origins for
                # current frame and mean frame, global shift of current frame, and the local warp
                # shift at this alignment point. The first two components are accounted for by dy,
                # dx.
                total_shift_y = int(round(dy - shift_y))
                total_shift_x = int(round(dx - shift_x))
                self.my_timer.stop('Stacking: compute AP shifts')

                # In debug mode: visualize shifted patch of the first AP and compare it with the
                # corresponding patch of the reference frame.
                if self.debug and not alignment_point_index:
                    frame_mono_blurred = self.frames.frames_mono_blurred(
                        frame_index)
                    y_low = alignment_point['patch_y_low']
                    y_high = alignment_point['patch_y_high']
                    x_low = alignment_point['patch_x_low']
                    x_high = alignment_point['patch_x_high']
                    reference_patch = (self.alignment_points.mean_frame[
                        y_low:y_high, x_low:x_high]).astype(uint16)
                    reference_patch = resize(reference_patch,
                                             None,
                                             fx=float(self.scale_factor),
                                             fy=float(self.scale_factor))

                    try:
                        # Cut out the globally stabilized and the de-warped patches
                        frame_stabilized = frame_mono_blurred[y_low +
                                                              dy:y_high + dy,
                                                              x_low +
                                                              dx:x_high + dx]
                        frame_stabilized = resize(frame_stabilized,
                                                  None,
                                                  fx=float(self.scale_factor),
                                                  fy=float(self.scale_factor))
                        font = FONT_HERSHEY_SIMPLEX
                        fontScale = 0.5
                        fontColor = (0, 255, 0)
                        lineType = 1
                        putText(frame_stabilized,
                                'stabilized: ' + str(dy) + ', ' + str(dx),
                                (5, 25), font, fontScale, fontColor, lineType)

                        frame_dewarped = frame_mono_blurred[
                            y_low + total_shift_y:y_high + total_shift_y,
                            x_low + total_shift_x:x_high + total_shift_x]
                        frame_dewarped = resize(frame_dewarped,
                                                None,
                                                fx=float(self.scale_factor),
                                                fy=float(self.scale_factor))
                        putText(
                            frame_dewarped,
                            'de-warped: ' + str(shift_y) + ', ' + str(shift_x),
                            (5, 25), font, fontScale, fontColor, lineType)
                        # Compose the three patches into a single image and send it to the
                        # visualization window.
                        composed_image = Miscellaneous.compose_image(
                            [
                                frame_stabilized, reference_patch,
                                frame_dewarped
                            ],
                            border=self.border)
                        self.update_image_window_signal.emit(composed_image)
                    except Exception as e:
                        print(str(e))

                    # Insert a delay to keep the current frame long enough in the visualization
                    # window.
                    sleep(self.image_delay)

                # Add the shifted alignment point patch to the AP's stacking buffer.
                self.my_timer.start('Stacking: remapping and adding')
                self.remap_rigid(frame, alignment_point['stacking_buffer'],
                                 total_shift_y, total_shift_x,
                                 alignment_point['patch_y_low'],
                                 alignment_point['patch_y_high'],
                                 alignment_point['patch_x_low'],
                                 alignment_point['patch_x_high'])
                self.my_timer.stop('Stacking: remapping and adding')

            # If there are holes between AP patches, add this frame's contribution (if any) to the
            # averaged background image.
            if self.number_stacking_holes > 0 and \
                    frame_index in self.align_frames.quality_sorted_indices[
                        :self.alignment_points.stack_size]:
                self.my_timer.start('Stacking: computing background')

                # Treat the case that the background is computed for specific patches only.
                if self.background_patches:
                    if self.frames.color:
                        for patch in self.background_patches:
                            self.averaged_background[patch['patch_y_low']:patch['patch_y_high'],
                                      patch['patch_x_low']:patch['patch_x_high'], :] += \
                                frame[patch['patch_y_low'] + self.align_frames.dy[frame_index] :
                                      patch['patch_y_high'] + self.align_frames.dy[frame_index],
                                      patch['patch_x_low'] + self.align_frames.dx[frame_index] :
                                      patch['patch_x_high'] + self.align_frames.dx[frame_index], :]
                    else:
                        for patch in self.background_patches:
                            self.averaged_background[patch['patch_y_low']:patch['patch_y_high'],
                                      patch['patch_x_low']:patch['patch_x_high']] += \
                                frame[patch['patch_y_low'] + self.align_frames.dy[frame_index] :
                                      patch['patch_y_high'] + self.align_frames.dy[frame_index],
                                      patch['patch_x_low'] + self.align_frames.dx[frame_index] :
                                      patch['patch_x_high'] + self.align_frames.dx[frame_index]]

                # The complete background image is computed.
                else:
                    if self.frames.color:
                        self.averaged_background += frame[
                            self.align_frames.dy[frame_index]:self.dim_y +
                            self.align_frames.dy[frame_index],
                            self.align_frames.dx[frame_index]:self.dim_x +
                            self.align_frames.dx[frame_index], :]
                    else:
                        self.averaged_background += frame[
                            self.align_frames.dy[frame_index]:self.dim_y +
                            self.align_frames.dy[frame_index],
                            self.align_frames.dx[frame_index]:self.dim_x +
                            self.align_frames.dx[frame_index]]
                self.my_timer.stop('Stacking: computing background')

        if self.progress_signal is not None:
            self.progress_signal.emit("Stack frames", 100)

        # Compute counters for shift distribution analysis.
        shift_counter = sum(self.shift_distribution)
        self.shift_entries_total = shift_counter + self.shift_failure_counter
        if self.shift_entries_total:
            self.shift_failure_percent = round(
                100. * self.shift_failure_counter / self.shift_entries_total,
                3)
        else:
            # If the value is <0, the percentage is not printed.
            self.shift_failure_percent = -1.

        # In debug mode: Close de-warp visualization window.
        if self.debug:
            self.terminate_image_window_signal.emit()

        # If a background image is being computed, divide the buffer by the number of contributions.
        if self.number_stacking_holes > 0:
            self.my_timer.start('Stacking: computing background')
            self.averaged_background /= self.alignment_points.stack_size
            self.my_timer.stop('Stacking: computing background')
    def stack_frames(self):
        """
        Compute the shifted contributions of all frames to all alignment points and add them to the
        appropriate alignment point stacking buffers.

        :return: -
        """

        # First find out if there are holes between AP patches.
        self.prepare_for_stack_blending()

        # Initialize the array for shift distribution statistics.
        self.shift_distribution = full(
            (self.configuration.alignment_points_search_width * 2, ),
            0,
            dtype=np_int)

        # In debug mode: Prepare for de-warp visualization.
        if self.debug:
            self.create_image_window_signal.emit()

        # Go through the list of all frames.
        for frame_index in range(self.frames.number):
            frame = self.frames.frames(frame_index)
            frame_mono_blurred = self.frames.frames_mono_blurred(frame_index)

            # After every "signal_step_size"th frame, send a progress signal to the main GUI.
            if self.progress_signal is not None and frame_index % self.signal_step_size == 1:
                self.progress_signal.emit(
                    "Stack frames",
                    int((frame_index / self.frames.number) * 100.))

            # Look up the constant shifts of the given frame with respect to the mean frame.
            dy = self.align_frames.dy[frame_index]
            dx = self.align_frames.dx[frame_index]

            # Go through all alignment points for which this frame was found to be among the best.
            for alignment_point_index in self.frames.used_alignment_points[
                    frame_index]:
                alignment_point = self.alignment_points.alignment_points[
                    alignment_point_index]

                # Compute the local warp shift for this frame.
                self.my_timer.start('Stacking: compute AP shifts')
                [shift_y, shift_x
                 ] = self.alignment_points.compute_shift_alignment_point(
                     frame_mono_blurred,
                     frame_index,
                     alignment_point_index,
                     de_warp=self.configuration.alignment_points_de_warp)

                # Increment the counter corresponding to the 2D warp shift.
                self.shift_distribution[int(
                    round(sqrt(shift_y**2 + shift_x**2)))] += 1

                # The total shift consists of three components: different coordinate origins for
                # current frame and mean frame, global shift of current frame, and the local warp
                # shift at this alignment point. The first two components are accounted for by dy,
                # dx.
                total_shift_y = int(round(dy - shift_y))
                total_shift_x = int(round(dx - shift_x))
                self.my_timer.stop('Stacking: compute AP shifts')

                # In debug mode: visualize shifted patch of the first AP and compare it with the
                # corresponding patch of the reference frame.
                if self.debug and not alignment_point_index:
                    frame_mono_blurred = self.frames.frames_mono_blurred(
                        frame_index)
                    y_low = alignment_point['patch_y_low']
                    y_high = alignment_point['patch_y_high']
                    x_low = alignment_point['patch_x_low']
                    x_high = alignment_point['patch_x_high']
                    reference_patch = (self.alignment_points.mean_frame[
                        y_low:y_high, x_low:x_high]).astype(uint16)
                    reference_patch = resize(reference_patch,
                                             None,
                                             fx=float(self.scale_factor),
                                             fy=float(self.scale_factor))

                    try:
                        # Cut out the globally stabilized and the de-warped patches
                        frame_stabilized = frame_mono_blurred[y_low +
                                                              dy:y_high + dy,
                                                              x_low +
                                                              dx:x_high + dx]
                        frame_stabilized = resize(frame_stabilized,
                                                  None,
                                                  fx=float(self.scale_factor),
                                                  fy=float(self.scale_factor))
                        font = FONT_HERSHEY_SIMPLEX
                        fontScale = 0.5
                        fontColor = (0, 255, 0)
                        lineType = 1
                        putText(frame_stabilized,
                                'stabilized: ' + str(dy) + ', ' + str(dx),
                                (5, 25), font, fontScale, fontColor, lineType)

                        frame_dewarped = frame_mono_blurred[
                            y_low + total_shift_y:y_high + total_shift_y,
                            x_low + total_shift_x:x_high + total_shift_x]
                        frame_dewarped = resize(frame_dewarped,
                                                None,
                                                fx=float(self.scale_factor),
                                                fy=float(self.scale_factor))
                        putText(
                            frame_dewarped,
                            'de-warped: ' + str(shift_y) + ', ' + str(shift_x),
                            (5, 25), font, fontScale, fontColor, lineType)
                        # Compose the three patches into a single image and send it to the
                        # visualization window.
                        composed_image = Miscellaneous.compose_image(
                            [
                                frame_stabilized, reference_patch,
                                frame_dewarped
                            ],
                            border=self.border)
                        self.update_image_window_signal.emit(composed_image)
                    except Exception as e:
                        print(str(e))

                    # Insert a delay to keep the current frame long enough in the visualization
                    # window.
                    sleep(self.image_delay)

                # Add the shifted alignment point patch to the AP's stacking buffer.
                self.my_timer.start('Stacking: remapping and adding')
                self.remap_rigid(frame, alignment_point['stacking_buffer'],
                                 total_shift_y, total_shift_x,
                                 alignment_point['patch_y_low'],
                                 alignment_point['patch_y_high'],
                                 alignment_point['patch_x_low'],
                                 alignment_point['patch_x_high'])
                self.my_timer.stop('Stacking: remapping and adding')

            # If there are holes between AP patches, add this frame's contribution (if any) to the
            # averaged background image.
            if self.number_stacking_holes > 0 and \
                    frame_index in self.align_frames.quality_sorted_indices[
                        :self.alignment_points.stack_size]:
                self.my_timer.start('Stacking: computing background')

                # Treat the case that the background is computed for specific patches only.
                if self.background_patches:
                    if self.frames.color:
                        for patch in self.background_patches:
                            self.averaged_background[patch['patch_y_low']:patch['patch_y_high'],
                                      patch['patch_x_low']:patch['patch_x_high'], :] += \
                                frame[patch['patch_y_low'] + self.align_frames.dy[frame_index] :
                                      patch['patch_y_high'] + self.align_frames.dy[frame_index],
                                      patch['patch_x_low'] + self.align_frames.dx[frame_index] :
                                      patch['patch_x_high'] + self.align_frames.dx[frame_index], :]
                    else:
                        for patch in self.background_patches:
                            self.averaged_background[patch['patch_y_low']:patch['patch_y_high'],
                                      patch['patch_x_low']:patch['patch_x_high']] += \
                                frame[patch['patch_y_low'] + self.align_frames.dy[frame_index] :
                                      patch['patch_y_high'] + self.align_frames.dy[frame_index],
                                      patch['patch_x_low'] + self.align_frames.dx[frame_index] :
                                      patch['patch_x_high'] + self.align_frames.dx[frame_index]]

                # The complete background image is computed.
                else:
                    if self.frames.color:
                        self.averaged_background += frame[
                            self.align_frames.dy[frame_index]:self.dim_y +
                            self.align_frames.dy[frame_index],
                            self.align_frames.dx[frame_index]:self.dim_x +
                            self.align_frames.dx[frame_index], :]
                    else:
                        self.averaged_background += frame[
                            self.align_frames.dy[frame_index]:self.dim_y +
                            self.align_frames.dy[frame_index],
                            self.align_frames.dx[frame_index]:self.dim_x +
                            self.align_frames.dx[frame_index]]
                self.my_timer.stop('Stacking: computing background')

        if self.progress_signal is not None:
            self.progress_signal.emit("Stack frames", 100)

        # In debug mode: Close de-warp visualization window.
        if self.debug:
            self.terminate_image_window_signal.emit()

        # If a background image is being computed, divide the buffer by the number of contributions.
        if self.number_stacking_holes > 0:
            self.my_timer.start('Stacking: computing background')
            self.averaged_background /= self.alignment_points.stack_size
            self.my_timer.stop('Stacking: computing background')