Example #1
0
def sub_pixel_shift_test():
    img = imread('../Images/Moon_Tile-024_043939_stacked_with_blurr_pp.tif',
                 IMREAD_GRAYSCALE)
    show_image("Original image", img, fullscreen=True)
    spx_shifty = 5.2
    spx_shiftx = 3.5

    img_resized, img_shifted = subpixel_shifted_frame(img, spx_shifty,
                                                      spx_shiftx)
    # for i in range(10):
    #     show_image("Image resized", img_resized, fullscreen=True)
    #     show_image("Image shifted", img_shifted, fullscreen=True)

    gauss_width_reference = 15
    gauss_width_frame = 19
    reference_frame_blurred_intermediate = GaussianBlur(
        img_resized, (gauss_width_reference, gauss_width_reference),
        0).astype(float32)
    reference_frame_blurred = GaussianBlur(
        reference_frame_blurred_intermediate,
        (gauss_width_reference, gauss_width_reference), 0).astype(float32)
    frame_blurred = GaussianBlur(img_shifted,
                                 (gauss_width_frame, gauss_width_frame), 0)

    y_ap = 170
    x_ap = 200
    half_box_width = 24
    y_low = y_ap - half_box_width
    y_high = y_ap + half_box_width
    x_low = x_ap - half_box_width
    x_high = x_ap + half_box_width
    reference_box_second_phase = reference_frame_blurred[y_low:y_high,
                                                         x_low:x_high]
    reference_box_first_phase = reference_box_second_phase[::2, ::2]

    search_width = 10
    shift_y_local_first_phase, shift_x_local_first_phase, success_first_phase, \
    shift_y_local_second_phase, shift_x_local_second_phase, success_second_phase = \
        Miscellaneous.multilevel_correlation(reference_box_first_phase, frame_blurred,
                                             gauss_width_frame,
                                             reference_box_second_phase, y_low, y_high, x_low, x_high,
                                             search_width,
                                             weight_matrix_first_phase=None, subpixel_solve=True)

    print("Shift in y, first phase: " + str(shift_y_local_first_phase) +
          ", second phase: " + str(shift_y_local_second_phase) + ", total: " +
          str(shift_y_local_first_phase + shift_y_local_second_phase))
    print("Shift in x, first phase: " + str(shift_x_local_first_phase) +
          ", second phase: " + str(shift_x_local_second_phase) + ", total: " +
          str(shift_x_local_first_phase + shift_x_local_second_phase))
Example #2
0
    def align_frames(self):
        """
        Compute the displacement of all frames relative to the sharpest frame using the alignment
        rectangle.

        :return: -
        """

        if self.configuration.align_frames_mode == "Surface":
            # For "Surface" mode the alignment rectangle has to be selected first.
            if self.x_low_opt is None:
                raise WrongOrderingError(
                    "Method 'align_frames' is called before 'select_alignment_rect'"
                )

            # From the sharpest frame cut out the alignment rectangle. The shifts of all other frames
            # will be computed relativ to this patch.
            if self.configuration.align_frames_method == "MultiLevelCorrelation":
                # MultiLevelCorrelation uses two reference windows with different resolution. Also,
                # please note that the data type is float32 in this case.
                reference_frame = self.frames.frames_mono_blurred(
                    self.frame_ranks_max_index).astype(float32)
                self.reference_window = reference_frame[
                    self.y_low_opt:self.y_high_opt,
                    self.x_low_opt:self.x_high_opt]
                # For the first phase a box with half the resolution is constructed.
                self.reference_window_first_phase = self.reference_window[::
                                                                          2, ::
                                                                          2]
            else:
                # For all other methods, the reference window is of type int32.
                reference_frame = self.frames.frames_mono_blurred(
                    self.frame_ranks_max_index).astype(int32)
                self.reference_window = reference_frame[
                    self.y_low_opt:self.y_high_opt,
                    self.x_low_opt:self.x_high_opt]

            self.reference_window_shape = self.reference_window.shape

        elif self.configuration.align_frames_mode == "Planet":
            # For "Planetary" mode compute the center of gravity for the reference image.
            cog_reference_y, cog_reference_x = AlignFrames.center_of_gravity(
                self.frames.frames_mono_blurred(self.frame_ranks_max_index))

        else:
            raise NotSupportedError("Frame alignment mode '" +
                                    self.configuration.align_frames_mode +
                                    "' not supported")

        # Initialize a list which for each frame contains the shifts in y and x directions.
        self.frame_shifts = [None] * self.frames.number

        # Initialize a counter of processed frames for progress bar signalling. It is set to one
        # because in the loop below the optimal frame is not counted.
        number_processed = 1

        # Loop over all frames. Begin with the sharpest (reference) frame
        for idx in chain(reversed(range(self.frame_ranks_max_index + 1)),
                         range(self.frame_ranks_max_index,
                               self.frames.number)):

            if idx == self.frame_ranks_max_index:
                # For the sharpest frame the displacement is 0 because it is used as the reference.
                self.frame_shifts[idx] = [0, 0]
                # Initialize two variables which keep the shift values of the previous step as
                # the starting point for the next step. This reduces the search radius if frames are
                # drifting.
                dy_min_cum = dx_min_cum = 0

            # For all other frames: Compute the global shift, using the "blurred" monochrome image.
            else:
                # After every "signal_step_size"th frame, send a progress signal to the main GUI.
                if self.progress_signal is not None and number_processed % self.signal_step_size == 1:
                    self.progress_signal.emit(
                        "Align all frames",
                        int(
                            round(10 * number_processed / self.frames.number) *
                            10))

                frame = self.frames.frames_mono_blurred(idx)

                # In Planetary mode the shift of the "center of gravity" of the image is computed.
                # This algorithm cannot fail.
                if self.configuration.align_frames_mode == "Planet":

                    cog_frame = AlignFrames.center_of_gravity(frame)
                    self.frame_shifts[idx] = [
                        cog_reference_y - cog_frame[0],
                        cog_reference_x - cog_frame[1]
                    ]

                # In Surface mode various methods can be used to measure the shift from one frame
                # to the next. The method "Translation" is special: Using phase correlation it is
                # the only method not based on a local search algorithm. It is treated differently
                # here because it does not require a re-shifting of the alignment patch.
                elif self.configuration.align_frames_method == "Translation":
                    # The shift is computed with cross-correlation. Cut out the alignment patch and
                    # compute its translation relative to the reference.
                    frame_window = self.frames.frames_mono_blurred(
                        idx)[self.y_low_opt:self.y_high_opt,
                             self.x_low_opt:self.x_high_opt]
                    self.frame_shifts[idx] = Miscellaneous.translation(
                        self.reference_window, frame_window,
                        self.reference_window_shape)

                # Now treat all "Surface" mode cases using local search algorithms. In each case
                # the result is the shift vector [dy_min, dx_min]. The search can fail (if within
                # the search radius no optimum is found). If that happens for at least one frame,
                # an exception is raised. The workflow thread then tries again using another
                # alignment patch.
                else:

                    if self.configuration.align_frames_method == "MultiLevelCorrelation":
                        # The shift is computed in two phases: First on a coarse pixel grid,
                        # and then on the original grid in a small neighborhood around the optimum
                        # found in the first phase.
                        shift_y_local_first_phase, shift_x_local_first_phase, \
                        success_first_phase, shift_y_local_second_phase, \
                        shift_x_local_second_phase, success_second_phase = \
                            Miscellaneous.multilevel_correlation(
                            self.reference_window_first_phase, frame,
                            self.configuration.frames_gauss_width,
                            self.reference_window, self.y_low_opt - dy_min_cum,
                                                          self.y_high_opt - dy_min_cum,
                                                          self.x_low_opt - dx_min_cum,
                                                          self.x_high_opt - dx_min_cum,
                            self.configuration.align_frames_search_width,
                            weight_matrix_first_phase=None)

                        success = success_first_phase and success_second_phase
                        if success:
                            [dy_min, dx_min] = [
                                shift_y_local_first_phase +
                                shift_y_local_second_phase,
                                shift_x_local_first_phase +
                                shift_x_local_second_phase
                            ]

                    elif self.configuration.align_frames_method == "RadialSearch":
                        # Spiral out from the shift position of the previous frame and search for the
                        # local optimum.
                        [dy_min,
                         dx_min], dev_r = Miscellaneous.search_local_match(
                             self.reference_window,
                             frame,
                             self.y_low_opt - dy_min_cum,
                             self.y_high_opt - dy_min_cum,
                             self.x_low_opt - dx_min_cum,
                             self.x_high_opt - dx_min_cum,
                             self.configuration.align_frames_search_width,
                             self.configuration.align_frames_sampling_stride,
                             sub_pixel=False)

                        # The search was not successful if a zero shift was reported after more
                        # than two search cycles.
                        success = len(dev_r) <= 2 or dy_min != 0 or dx_min != 0

                    elif self.configuration.align_frames_method == "SteepestDescent":
                        # Spiral out from the shift position of the previous frame and search for the
                        # local optimum.
                        [dy_min, dx_min
                         ], dev_r = Miscellaneous.search_local_match_gradient(
                             self.reference_window, frame,
                             self.y_low_opt - dy_min_cum,
                             self.y_high_opt - dy_min_cum,
                             self.x_low_opt - dx_min_cum,
                             self.x_high_opt - dx_min_cum,
                             self.configuration.align_frames_search_width,
                             self.configuration.align_frames_sampling_stride,
                             self.dev_table)

                        # The search was not successful if a zero shift was reported after more
                        # than two search cycles.
                        success = len(dev_r) <= 2 or dy_min != 0 or dx_min != 0

                    else:
                        raise NotSupportedError(
                            "Frame alignment method " +
                            configuration.align_frames_method +
                            " not supported")

                    # If the local search was unsuccessful, quit the frame loop with an error.
                    if not success:
                        raise InternalError("frame " + str(idx))

                    # Update the cumulative shift values to be used as starting point for the
                    # next frame.
                    dy_min_cum += dy_min
                    dx_min_cum += dx_min
                    self.frame_shifts[idx] = [dy_min_cum, dx_min_cum]

                    # If the alignment window gets too close to a frame edge, move it away from
                    # that edge by half the border width. First check if the reference window still
                    # fits into the shifted frame.
                    if self.shape[0] - abs(
                            dy_min_cum) - 2 * self.configuration.align_frames_search_width - \
                            self.configuration.align_frames_border_width < \
                            self.reference_window_shape[0] or self.shape[1] - abs(
                            dx_min_cum) - 2 * self.configuration.align_frames_search_width - \
                            self.configuration.align_frames_border_width < \
                            self.reference_window_shape[1]:
                        raise ArgumentError(
                            "Frame stabilization window does not fit into"
                            " intersection")

                    new_reference_window = False
                    # Start with the lower y edge.
                    while self.y_low_opt - dy_min_cum < \
                            self.configuration.align_frames_search_width + \
                            self.configuration.align_frames_border_width / 2:
                        self.y_low_opt += ceil(
                            self.configuration.align_frames_border_width / 2.)
                        self.y_high_opt += ceil(
                            self.configuration.align_frames_border_width / 2.)
                        new_reference_window = True
                    # Now the upper y edge.
                    while self.y_high_opt - dy_min_cum > self.shape[
                        0] - self.configuration.align_frames_search_width - \
                            self.configuration.align_frames_border_width / 2:
                        self.y_low_opt -= ceil(
                            self.configuration.align_frames_border_width / 2.)
                        self.y_high_opt -= ceil(
                            self.configuration.align_frames_border_width / 2.)
                        new_reference_window = True
                    # Now the lower x edge.
                    while self.x_low_opt - dx_min_cum < \
                            self.configuration.align_frames_search_width + \
                            self.configuration.align_frames_border_width / 2:
                        self.x_low_opt += ceil(
                            self.configuration.align_frames_border_width / 2.)
                        self.x_high_opt += ceil(
                            self.configuration.align_frames_border_width / 2.)
                        new_reference_window = True
                    # Now the upper x edge.
                    while self.x_high_opt - dx_min_cum > self.shape[
                        1] - self.configuration.align_frames_search_width - \
                            self.configuration.align_frames_border_width / 2:
                        self.x_low_opt -= ceil(
                            self.configuration.align_frames_border_width / 2.)
                        self.x_high_opt -= ceil(
                            self.configuration.align_frames_border_width / 2.)
                        new_reference_window = True

                    # If the window was moved, update the "reference window(s)".
                    if new_reference_window:
                        if self.configuration.align_frames_method == "MultiLevelCorrelation":
                            self.reference_window = reference_frame[
                                self.y_low_opt:self.y_high_opt,
                                self.x_low_opt:self.x_high_opt]
                            # For the first phase a box with half the resolution is constructed.
                            self.reference_window_first_phase = self.reference_window[::
                                                                                      2, ::
                                                                                      2]
                        else:
                            self.reference_window = reference_frame[
                                self.y_low_opt:self.y_high_opt,
                                self.x_low_opt:self.x_high_opt]

                # This frame is processed, go to next one.
                number_processed += 1

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

        # Compute the shape of the area contained in all frames in the form [[y_low, y_high],
        # [x_low, x_high]]
        self.intersection_shape = [[
            max(b[0] for b in self.frame_shifts),
            min(b[0] for b in self.frame_shifts) + self.shape[0]
        ],
                                   [
                                       max(b[1] for b in self.frame_shifts),
                                       min(b[1] for b in self.frame_shifts) +
                                       self.shape[1]
                                   ]]