예제 #1
0
    def debug_rotation(ast_trial):
        """
        Plot what the values look like when rotated, debugging tool
        """
        try:
            if ast_trial.is_ast_trial:
                rotated_points = AsteriskCalculations.rotate_points(
                    ast_trial.poses, AsteriskCalculations.rotations[
                        ast_trial.trial_translation])
            else:
                rotated_points = ast_trial
        except:
            rotated_points = ast_trial

        # plot the points
        x = pd.Series.to_list(rotated_points["x"].dropna())
        y = pd.Series.to_list(rotated_points["y"].dropna())

        plt.plot(x, y, color="xkcd:dark orange")

        # plot the target line
        tar_x, tar_y = AsteriskPlotting.get_c(100)
        plt.plot(tar_x, tar_y, color="xkcd:dark blue")

        # show plot
        plt.show()
예제 #2
0
    def generate_target_line(self, n_samples=100):
        """
        Using object trajectory (self.poses), build a line to compare to for frechet distance.
        Updates this attribute on object.
        :param n_samples: number of samples for target line. Defaults to 100
        """
        x_vals, y_vals = aplt.get_direction(self.trial_translation, n_samples)

        target_line = np.column_stack((x_vals, y_vals))

        # get last object pose and use it for determining how far target line should go
        last_obj_pose = self.poses.tail(1).to_numpy()[0]

        target_line_length = acalc.narrow_target(last_obj_pose, target_line)

        if target_line_length:
            distance_travelled = acalc.t_distance(
                [0, 0], target_line[target_line_length + 1])
            final_target_ln = target_line[:target_line_length]
        else:
            distance_travelled = acalc.t_distance([0, 0], target_line[0])
            final_target_ln = target_line[:1]

        # TODO: distance travelled has error because it is built of target line... maybe use last_obj_pose instead?
        return final_target_ln, distance_travelled
예제 #3
0
    def calc_max_area_region(ast_trial, percent_window_size=0.2):
        """
        Calculates the area of max error by sliding a window of 20% normalized length along the target line
        Seems that 10% is too small in regions of fast movement
        """
        # TODO: cheating again by rotating points... what about negative values?
        points = AsteriskCalculations.rotate_points(ast_trial.poses, AsteriskCalculations.rotations[ast_trial.trial_translation])
        t_x, t_y = AsteriskPlotting.get_c(100)  # TODO: maybe make target_line a pandas dataframe
        targets = np.column_stack((t_x, t_y))

        #AsteriskMetrics.debug_rotation(points)

        # prepare bound size
        bound_size = 0.5 * (percent_window_size * ast_trial.total_distance)
        x_center = bound_size + 0  # just being explicit here -> x_min is 0
        x_max = 2 * bound_size
        max_area_calculated = 0
        x_center_at_max = x_center

        while x_max <= ast_trial.total_distance:
            # print(f"Now at {x_center} || {x_max}/{ast_trial.total_distance}")
            bounded_points = AsteriskCalculations.get_points_df(points, x_center, bound_size)
            b_x = pd.Series.to_list(bounded_points["x"].dropna())
            b_y = pd.Series.to_list(bounded_points["y"].dropna())
            bounded_points_not_df = np.column_stack((b_x, b_y))

            target_points = AsteriskCalculations.get_points_list(targets, x_center, bound_size)

            try:
                area_calculated = sm.area_between_two_curves(bounded_points_not_df, target_points)
            except ValueError or IndexError:
                # usually this triggers if there aren't enough points (more than one) in the window
                # if there aren't enough points, make enough points!
                try:
                    area_calculated = AsteriskCalculations.interpolate_points(points, x_center,
                                                                              bounded_points, bound_size,
                                                                              target_points)

                    print("Successful interpolation!")
                except Exception as e:
                    print("Interpolation Failed.")
                    print(e)
                    # if not points were found at all in this region, depending on bound size
                    area_calculated = 0

            x_center = x_center + 0.1 * bound_size  # want to step in 1% increments
            x_max = x_center + bound_size

            if np.abs(area_calculated) > max_area_calculated:
                max_area_calculated = np.abs(area_calculated)
                x_center_at_max = x_center

        # percentage along the target_line line that the center of max error was located
        x_center_perc = x_center_at_max / 0.5  # gives us percentage along the full target line, for easy comparing

        # x_center_at_max_r = AsteriskMetrics.rotate_point([x_center_at_max, 0],
        #                                                  -1 * AsteriskMetrics.rotations[ast_trial.trial_translation])

        # print(f"results: {max_area_calculated}, {x_center_perc}")
        return max_area_calculated, x_center_perc
예제 #4
0
    def plot_direction(self,
                       translation,
                       rotation="n",
                       show_plot=True,
                       save_plot=False):
        """
        Plot the average path for each hand data object contained in this object for a specific
        translation rotation pair.
        """
        colors = [
            "tab:blue", "tab:purple", "tab:red", "tab:olive", "tab:cyan",
            "tab:green", "tab:pink", "tab:orange"
        ]

        # grab the average values from all hands
        avg_label = f"{translation}_{rotation}"
        averages_to_plot = []
        hand_order = []

        for h in self.hands:
            hand_order.append(h.hand.get_name())

            for a in h.averages:
                a_label = f"{a.trial_translation}_{a.trial_rotation}"

                if a_label == avg_label:
                    averages_to_plot.append(a)

        for i, a in enumerate(averages_to_plot):
            a_x, a_y, _ = a.get_poses()

            plt.plot(a_x, a_y, color=colors[i], label=hand_order[i])

        # plot the straight line
        t_x, t_y = AsteriskPlotting.get_direction(translation)
        plt.plot(t_x, t_y, color="r", linestyle="dashed")

        if save_plot:
            # added the zero to guarantee that it comes first
            plt.savefig(f"pics/0all_{rotation}.jpg", format='jpg')
            # name -> tuple: subj, hand  names
            print("Figure saved.")
            print(" ")

        if show_plot:
            plt.legend()
            plt.show()

        return plt
예제 #5
0
    def plot_all_target_lines(self, order_of_colors):
        """
        Plot all target lines on a plot for easy reference
        :param order_of_colors:
        """
        x_a, y_a = aplt.get_a()
        x_b, y_b = aplt.get_b()
        x_c, y_c = aplt.get_c()
        x_d, y_d = aplt.get_d()
        x_e, y_e = aplt.get_e()
        x_f, y_f = aplt.get_f()
        x_g, y_g = aplt.get_g()
        x_h, y_h = aplt.get_h()

        ideal_xs = [x_a, x_b, x_c, x_d, x_e, x_f, x_g, x_h]
        ideal_ys = [y_a, y_b, y_c, y_d, y_e, y_f, y_g, y_h]

        for i in range(8):
            plt.plot(ideal_xs[i], ideal_ys[i], color=order_of_colors[i], label='ideal', linestyle='--')
예제 #6
0
    def plot_orientation_errors(self, translation, subject=None, rotation="n", show_plot=True, save_plot=False):
        """
        line plot of orientation error throughout a trial for a specific direction
        :param translation: the type of translation
        :param subject: list of subjects. If none is provided, uses all of them
        :param rotation: type of rotation. Defaults to "n"
        :param show_plot: flag to show plot. Default is true
        :param save_plot: flat to save plot as a file. Default is False
        """
        if subject:
            trials = self._get_ast_dir(translation, subject, rotation)
        else:
            trials = self._get_ast_dir(translation, self.subjects_containing, rotation)

        # if self.averages and incl_avg:  # TODO: have an option to include the average?
        #     for a in self.averages:
        #         if a.trial_translation==direction_label and a.trial_rotation==rotation_label:
        #             trials.append(a)

        for t in trials:
            rot_err = t.calc_rot_err()
            # currently using the get_c function to generate a normalized set of x values to use as x values
            x, _ = aplt.get_c(len(rot_err))  # will need to multiply by 2 to get it to go to 1.0 instead of 0.5
            plt.plot(2*x, rot_err, label=f"Orientation Err {t.subject}, trial {t.trial_num}")

        if save_plot:
            if subject:
                plt.savefig(f"pics/angerror_{self.hand.get_name()}_{subject}_{translation}_{rotation}.jpg", format='jpg')
            else:
                plt.savefig(f"pics/angerror_{self.hand.get_name()}_all_{translation}_{rotation}.jpg", format='jpg')
            # name -> tuple: subj, hand  names
            print("Figure saved.")
            print(" ")

        if show_plot:
            plt.legend()
            plt.show()
예제 #7
0
    def plot_trial(self, use_filtered=True, show_plot=True, save_plot=False):
        """
        Plot the poses in the trial, using marker size to denote the error in twist from the desired twist
        :param use_filtered: Gives option to return filtered or unfiltered data
        :param show_plot: flag to show plot. Default is true
        :param save_plot: flat to save plot as a file. Default is False
        """
        data_x, data_y, theta = self.get_poses(use_filtered)

        # experimenting...
        # junk = 70
        # data_x = data_x[0:(len(data_x)-junk)]
        # data_y = data_y[0:(len(data_y)-junk)]

        plt.plot(data_x, data_y, color="xkcd:dark blue", label='trajectory')

        # plot data points separately to show angle error with marker size
        for n in range(len(data_x)):
            # TODO: rn having difficulty doing marker size in a batch, so plotting each point separately
            plt.plot(data_x[n],
                     data_y[n],
                     'r.',
                     alpha=0.5,
                     markersize=5 * theta[n])

        target_x, target_y = [], []
        for t in self.target_line:
            target_x.append(t[0])
            target_y.append(t[1])

        #target_x, target_y = aplt.get_direction(self.trial_translation)
        plt.plot(target_x,
                 target_y,
                 color="xkcd:pastel blue",
                 label="target_line",
                 linestyle="-")

        max_x = max(data_x)
        max_y = max(data_y)
        min_x = min(data_x)
        min_y = min(data_y)

        plt.xlabel('X')
        plt.ylabel('Y')
        plt.title('Path of Object')

        # gives a realistic view of what the path looks like
        plt.xticks(np.linspace(aplt.round_half_down(min_x, decimals=2),
                               aplt.round_half_up(max_x, decimals=2), 10),
                   rotation=30)
        if self.trial_translation in ["a", "b", "c", "g", "h"]:
            plt.yticks(
                np.linspace(0, aplt.round_half_up(max_y, decimals=2), 10))
        else:
            plt.yticks(
                np.linspace(aplt.round_half_down(min_y, decimals=2), 0, 10))

        # experimenting...
        # plt.xticks(np.linspace(-10,
        #                        80, 10), rotation=30)
        # plt.yticks(np.linspace(-10, 80, 10))

        # plt.gca().set_aspect('equal', adjustable='box')

        plt.title(f"Plot: {self.generate_name()}")

        if save_plot:
            plt.savefig(f"pics/plot_{self.generate_name()}.jpg", format='jpg')
            # name -> tuple: subj, hand  names
            print("Figure saved.")
            print(" ")

        if show_plot:
            plt.legend()
            plt.show()
예제 #8
0
    def check_no_backtracking(data,
                              translation_label,
                              threshold=5,
                              debug_rotation=False):
        """
        True if no (or little) backtracking, False is there is

        parameters
        ----------
        - data - dataframe, not normalized yet
        - threshold - how much backtracking to allow, default is 10 mm

        returns
        -------
        - decision - (True/False that there's no significant backtracking)
        - (the most backtracking, threshold) - provided for debugging,
        when True provides the highest cumulated backtrack
        TODO: test this one more thoroughly!
        """
        # convert to mm
        data = data * [1., 1., 1., 1000., 1000., 1000., 1., 1000.]
        data = data.round(4)

        # rotate everything to c direction
        rotated_df = AsteriskCalculations.rotate_points(
            data, AsteriskCalculations.rotations[translation_label])

        if debug_rotation:
            # plot the points
            x = pd.Series.to_list(rotated_df["x"].dropna())
            y = pd.Series.to_list(rotated_df["y"].dropna())

            plt.plot(x, y, color="xkcd:dark orange")

            # plot the target line
            tar_x, tar_y = AsteriskPlotting.get_c(100)
            plt.plot(tar_x * 100, tar_y * 100, color="xkcd:dark blue")

            # show plot
            plt.show()

        # calculate the delta x between each point
        dxs = []  # hold the delta x vals
        prev_x = 0
        for p in rotated_df.iterrows():
            x = p[1]['x']
            dx = x - prev_x
            dxs.append(dx)

            prev_x = x

        rotated_df["dx"] = dxs

        # for those in the wrong direction, see how much accumulates and how far they get
        c_val = 0  # variable which stores cumulative values
        max_c_val = 0
        for p in rotated_df.iterrows():
            if p[1]["dx"] < 0:
                c_val = c_val + p[1]["dx"]

                if c_val > threshold:
                    return False, (c_val, threshold)

                if abs(c_val) > abs(max_c_val):
                    max_c_val = c_val
            else:
                c_val = 0

            print(f"c:{c_val} for {p[1]['x']}, {p[1]['dx']}")

        # if we went through the entire path and found no significant backtracking, then True!
        return True, (c_val, threshold)