Beispiel #1
0
def plot_line(xdata,
              ydata,
              title,
              xlbl,
              ylbl,
              out_name,
              color,
              lbl,
              stdev=None,
              do_save=True,
              black_bg=False):

    #####
    # Plot
    #####

    plt.plot(xdata, ydata, 'o', markersize=5, markeredgewidth=0, color=color)
    #hdl, =
    plt.plot(xdata, ydata, '-', linewidth=2, color=color, label=lbl)

    # Plot error bars
    #   http://matplotlib.org/1.2.1/examples/pylab_examples/errorbar_demo.html
    #   Plot error bar without line, fmt='':
    #   http://stackoverflow.com/questions/18498742/how-do-you-make-an-errorbar-plot-in-matplotlib-using-linestyle-none-in-rcparams
    if stdev:
        plt.errorbar(xdata, ydata, fmt='', capthick=2, yerr=stdev, color=color)

    plt.grid(True, color='gray')
    #plt.grid (False)

    if title:
        plt.title(title)
    plt.xlabel(xlbl)
    plt.ylabel(ylbl)

    if black_bg:
        black_background()

    # Save to file
    if do_save:
        plt.savefig(out_name, bbox_inches='tight')
        print('Plot saved to %s' % out_name)
def plot_hist(
        hist,
        edges,
        ax,
        ylbl,
        strict_lims=True,  #, xlbl_suffix=''):
        bg_color='white',
        fg_color='g',
        tick_rot=0):

    # MATLAB "hold on"
    ax.hold(True)
    # MATLAB "grid on"
    ax.grid(True, color='gray')

    # Ref font size of labels: http://stackoverflow.com/questions/12444716/how-do-i-set-figure-title-and-axes-labels-font-size-in-matplotlib
    #ax.set_xlabel ('Bins' + xlbl_suffix, fontsize=6)
    ax.set_ylabel(ylbl)

    if strict_lims:
        ax.set_xlim(edges[0], edges[len(edges) - 1])

    # Get histogram centers from edges. n+1 edges means there are n centers.
    centers = (edges[0:len(edges) - 1] + edges[1:len(edges)]) * 0.5

    width = (edges[1] - edges[0]) * .5

    #print (edges)
    #print (centers)
    #print (hist)
    ax.bar(centers, hist, width=width, color=fg_color, edgecolor='none')

    # Rotate tick orientation
    # Diagonal 45-degree slanted tick labels
    # Ref: https://stackoverflow.com/questions/14852821/aligning-rotated-xticklabels-with-their-respective-xticks/14854007#14854007
    # axes API: https://matplotlib.org/api/axes_api.html
    if tick_rot != 0:
        ax.set_xticklabels(ax.get_xticks(), rotation=tick_rot)

    if bg_color == 'black':
        black_background(ax=ax)
Beispiel #3
0
def main():

    arg_parser = argparse.ArgumentParser()

    # Variable number of args http://stackoverflow.com/questions/13219910/argparse-get-undefined-number-of-arguments
    arg_parser.add_argument(
        '--display',
        action="store_true",
        help=
        'Specify for debugging one by one. Displays matplotlib plots. This will block program flow and require user interaction to close window to move on to next data sample.'
    )
    arg_parser.add_argument('--no-save',
                            action='store_true',
                            help='Specify to NOT save any visualized images.')
    arg_parser.add_argument(
        '--uinput',
        action='store_true',
        help='Specify to use keyboard interaction, useful for debugging.')
    arg_parser.add_argument(
        '--scale-heatmaps',
        action='store_true',
        help=
        'Specify the flag if it was specified to occlusion_test.cpp to generate the heatmaps. Rescale heatmaps back to min/max depth values.'
    )
    arg_parser.add_argument('--black-bg',
                            action='store_true',
                            help='Generate plots with black background.')

    args = arg_parser.parse_args()

    SCALE_HEATMAPS = args.scale_heatmaps
    BLACK_BG = args.black_bg

    DISPLAY_IMAGES = args.display
    UINPUT = args.uinput

    SAVE_IMG = not args.no_save
    # Number of grasps per object to save images
    N_TO_SAVE = 1

    # User adjust parameter. Save just a single axis, for paper or presentation
    # Indices in the 3 subplots
    DEPTH_IDX = 1
    VIS_IDX = 2
    OCC_IDX = 3
    # NOTE: Make sure these names match with the subplot indices!
    SUBPLOT_NAMES = [get_vis_depth_fmt(), get_vis_vis_fmt(), get_vis_occ_fmt()]
    #SAVE_SUBPLOTS = [DEPTH_IDX]
    SAVE_SUBPLOTS = [DEPTH_IDX, VIS_IDX, OCC_IDX]

    pkg_path = rospkg.RosPack().get_path('depth_scene_rendering')
    scene_list_path = os.path.join(pkg_path, "config/scenes_noisy.yaml")
    scene_list_f = open(scene_list_path, 'rb')

    renders_dir = get_renders_data_path()
    heatmaps_dir = get_heatmaps_data_path()

    heatmap_fmts = get_heatmap_blob_fmt()
    vis_fmt = heatmap_fmts[0]
    occ_fmt = heatmap_fmts[1]

    scaler = RawDepthScaling()
    success, depth_range = scaler.load_depth_range()
    if not success:
        return
    MIN_DEPTH = depth_range[0]
    MAX_DEPTH = depth_range[1]

    vis_dir = get_vis_path()
    contacts_dir = get_contacts_path()

    # scenes.yaml
    objs = ConfigReadYAML.read_scene_paths()
    # String
    obj_names = objs[0]
    # List of list of strings, paths to .pcd scene files
    scene_paths = objs[2]

    # For each object
    terminate = False
    for o_i in range(len(obj_names)):
        #for o_i in [7]:

        obj_name = obj_names[o_i]

        # Contacts meta file, number of elements in the list is number of grasps
        obj_meta_path = os.path.join(contacts_dir, obj_name + '_meta.csv')
        with open(obj_meta_path, 'rb') as obj_meta_f:
            obj_meta_reader = csv.reader(obj_meta_f)
            # Only 1 row in file. List of strings separated by comma
            for row in obj_meta_reader:
                n_grasps = len(row)

        skip_obj = False
        # For each scene for this object
        for s_i in range(len(scene_paths[o_i])):

            # For each grasp for this object
            for g_i in range(n_grasps):

                scene_path = scene_paths[o_i][s_i]
                scene_base = os.path.basename(scene_path)

                print(
                    'Object [%d], Scene [%d], Grasp [%d], Loading triplet files for %s'
                    % (o_i, s_i, g_i, scene_path))

                depth_name = os.path.join(
                    renders_dir,
                    os.path.splitext(scene_base)[0] + 'crop.png')
                vis_name = os.path.join(
                    heatmaps_dir,
                    vis_fmt % (os.path.splitext(scene_base)[0], g_i))
                occ_name = os.path.join(
                    heatmaps_dir,
                    occ_fmt % (os.path.splitext(scene_base)[0], g_i))

                depth_im = np_from_depth(depth_name)
                vis_im = np_from_depth(vis_name)
                occ_im = np_from_depth(occ_name)

                if depth_im is None or vis_im is None or occ_im is None:
                    print(
                        '%sERROR: One or more of (depth, vis_tac, occ_tac) images does not exist. Did you forget to run rosrun depth_scene_generation postprocess_scenes? Run it to generate the cropped images. Terminating...%s'
                        % (ansi.FAIL, ansi.ENDC))
                    return

                # Calculate raw depths from the integers in image
                depth_im = scaler.scale_ints_to_depths(depth_im)
                if SCALE_HEATMAPS:
                    vis_im = scaler.scale_ints_to_depths(vis_im)
                    occ_im = scaler.scale_ints_to_depths(occ_im)
                else:
                    vis_im = vis_im.astype(np.float32) / 255.0
                    occ_im = occ_im.astype(np.float32) / 255.0

                fig = plt.figure(figsize=(15, 6))

                ax = plt.subplot(1, 3, 1)
                # gray_r
                depth_obj = plt.imshow(depth_im[:, :, 0], cmap=plt.cm.jet)
                #clim=[MIN_DEPTH, MAX_DEPTH])
                tt1 = plt.title('Raw Depth')
                cb1 = plt.colorbar(depth_obj, fraction=0.046, pad=0.01)
                if BLACK_BG:
                    black_background(title_hdl=tt1)
                    black_colorbar(cb1)

                ax = plt.subplot(1, 3, 2)
                # Plot a white background first, so that black background plots produce
                #   the same heatmap appearances as white background ones.
                # Ref plot white image https://stackoverflow.com/questions/28234416/plotting-a-white-grayscale-image-in-python-matplotlib
                plt.imshow(np.ones((depth_im.shape[0], depth_im.shape[1])),
                           cmap='gray',
                           vmin=0,
                           vmax=1,
                           alpha=1.0)
                plt.imshow(depth_im[:, :, 0], cmap=plt.cm.jet, alpha=0.4)
                vis_obj = plt.imshow(vis_im[:, :, 0],
                                     cmap=plt.cm.jet,
                                     alpha=0.7)
                #clim=[MIN_DEPTH, MAX_DEPTH])
                tt2 = plt.title('Visible')
                cb2 = plt.colorbar(vis_obj, fraction=0.046, pad=0.01)
                if BLACK_BG:
                    black_background(title_hdl=tt2)
                    black_colorbar(cb2)

                ax = plt.subplot(1, 3, 3)
                plt.imshow(np.ones((depth_im.shape[0], depth_im.shape[1])),
                           cmap='gray',
                           vmin=0,
                           vmax=1,
                           alpha=1.0)
                plt.imshow(depth_im[:, :, 0], cmap=plt.cm.jet, alpha=0.4)
                occ_obj = plt.imshow(occ_im[:, :, 0],
                                     cmap=plt.cm.jet,
                                     alpha=0.7)
                #clim=[MIN_DEPTH, MAX_DEPTH])
                tt3 = plt.title('Occluded')
                # Flush colorbar with image
                cb3 = plt.colorbar(occ_obj, fraction=0.046, pad=0.01)
                if BLACK_BG:
                    black_background(title_hdl=tt3)
                    black_colorbar(cb3)

                fig.tight_layout()

                if SAVE_IMG:
                    dest = os.path.join(
                        vis_dir,
                        get_vis_heatmap_fmt() % (os.path.splitext(
                            os.path.basename(scene_base))[0], g_i))

                    if BLACK_BG:
                        plt.savefig(dest,
                                    bbox_inches='tight',
                                    facecolor=fig.get_facecolor(),
                                    edgecolor='none',
                                    transparent=True)
                    else:
                        fig.savefig(dest)
                    print('%sWritten entire plot to %s%s' %
                          (ansi.OKCYAN, dest, ansi.ENDC))

                # Save an individual axis
                for subplot_i in SAVE_SUBPLOTS:

                    # For depth image, only need to save 1st one, `.` all the same
                    if subplot_i == DEPTH_IDX and g_i != 0:
                        continue

                    ax = plt.subplot(1, 3, subplot_i)
                    ax.set_aspect(1)
                    plt.axis('off')

                    # To save individual axis cleanly
                    depth_dest = os.path.join(
                        vis_dir, SUBPLOT_NAMES[subplot_i - 1] %
                        (os.path.splitext(os.path.basename(scene_base))[0]))
                    extent = ax.get_window_extent().transformed(
                        fig.dpi_scale_trans.inverted())
                    fig.savefig(depth_dest, bbox_inches=extent)
                    print('%sWritten depth image axis to %s%s' %
                          (ansi.OKCYAN, depth_dest, ansi.ENDC))

                if DISPLAY_IMAGES:
                    plt.show()

                plt.close(fig)

                if g_i >= N_TO_SAVE - 1:
                    break

                if UINPUT:
                    uinput = raw_input(
                        'Press s to skip to next scene, o to skip to next objet, q to quit, or anything else to go to next grasp in this scene: '
                    )
                    if uinput.lower() == 's':
                        break
                    elif uinput.lower() == 'o':
                        skip_obj = True
                        break
                    elif uinput.lower() == 'q':
                        terminate = True
                        break

            # Break out of s_i
            if skip_obj or terminate:
                break

        # Break out of o_i
        if terminate:
            break
Beispiel #4
0
def main():

    ORDERED = False
    TOP_ONLY = True

    BLACK_BG = True

    plot_all = False

    if ORDERED:
        pos, quats = test_ordered_poses(topOnly=TOP_ONLY)
        if TOP_ONLY:
            out_name = 'test_spherical_pose_generation_top'
        else:
            out_name = 'test_spherical_pose_generation_full'

    else:
        n_rand_pts = 100
        pos, quats = test_rand_poses(n_rand_pts, topOnly=TOP_ONLY)

        if TOP_ONLY:
            out_name = 'test_spherical_pose_generation_rand_top'
        else:
            out_name = 'test_spherical_pose_generation_rand_full'

    #print quats.shape
    n_pts = quats.shape[1]

    #####
    # Use quaternion rotation only, plot rotations on unit sphere

    # 3 x n
    XYZ = np.tile(np.array([[0, 0, 0]]).T, (1, n_pts))
    # 3 x n
    UVW = np.zeros((3, n_pts))

    for i in range(n_pts):

        mat = quaternion_matrix(quats[:, i])

        # Multiply the rotation by x-axis, to get a vector
        UVW[:, i] = np.dot(mat[0:3, 0:3], [1, 0, 0])

    if plot_all:

        fig = plt.figure(figsize=(15, 6))

        # Ref 3D plot https://matplotlib.org/mpl_toolkits/mplot3d/tutorial.html
        ax = fig.add_subplot(131, projection='3d')

        # Ref https://matplotlib.org/examples/mplot3d/quiver3d_demo.html
        # First three xyzs are start point, last three are vector.
        # pivot specifies the part of the arrow that is at the grid point, 1st xyzs
        #   passed in. Arrow rotates about this point. Default is 'tip', regardless
        #   of what API says. Arrowheads end at first set of xyzs passed in. Other
        #   option is 'tail' or 'middle'.
        ax.quiver(UVW[0, :],
                  UVW[1, :],
                  UVW[2, :],
                  -UVW[0, :],
                  -UVW[1, :],
                  -UVW[2, :],
                  color='orange',
                  length=0.4,
                  arrow_length_ratio=0.5,
                  pivot='tail')
        ax.scatter(UVW[0, :], UVW[1, :], UVW[2, :], c='orange')

        ax.set_title('By quaternion only')
        ax.set_aspect(1)
        ax.set_xlim(-1, 1)
        ax.set_ylim(-1, 1)
        ax.set_zlim(-1, 1)

        #####
        # Use position only, plot positions on sphere

        ax = fig.add_subplot(132, projection='3d')

        ax.scatter(pos[0, :], pos[1, :], pos[2, :], c='red')

        ax.set_title('By position only')
        ax.set_aspect(1)
        ax.set_xlim(-1, 1)
        ax.set_ylim(-1, 1)
        ax.set_zlim(-1, 1)

        #####
        # Overlap position and quaternion, to see if they overlap exactly
        # Result: They do NOT overlap exactly. So the best option to generate both
        #   position and quaternion is using the quaternion way, where quats variable
        #   gives the quaternions, UVW gives the positions.

        ax = fig.add_subplot(133, projection='3d')

        # Quaternion
        #ax.quiver (XYZ[0, :], XYZ[1, :], XYZ[2, :], UVW[0, :], UVW[1, :], UVW[2, :],
        #  color='orange', length=1, arrow_length_ratio=0.1, pivot='tail')
        ax.scatter(UVW[0, :], UVW[1, :], UVW[2, :], c='orange', alpha=0.5)

        # Position
        ax.scatter(pos[0, :], pos[1, :], pos[2, :], c='red', alpha=0.5)

        # Text label, for easier debugging of mismatches between positions and quats
        for i in range(pos.shape[1]):
            ax.text(UVW[0, i], UVW[1, i], UVW[2, i], str(i), color='orange')
            ax.text(pos[0, i], pos[1, i], pos[2, i], str(i), color='red')

        ax.set_title('Overlay quaternion and position')
        ax.set_aspect(1)
        ax.set_xlim(-1, 1)
        ax.set_ylim(-1, 1)
        ax.set_zlim(-1, 1)

        fig.tight_layout()
        '''
    # Save png
    fig.savefig (out_name + '.png')
    print ('Written plot to %s.png' % out_name)
 
    # Save eps
    fig.savefig (out_name + '.eps')
    print ('Written plot to %s.eps' % out_name)
    '''

        plt.show()

    # Plot an individual image for thesis writing

    fig2 = plt.figure()
    ax = fig2.add_subplot(111, projection='3d')

    cm_name = custom_colormap_neon()
    color = mpl_color(1, 8, colormap_name=cm_name)
    #color = np.array ((252, 149,  11)) / 255.0

    # Ref https://matplotlib.org/examples/mplot3d/quiver3d_demo.html
    # First three xyzs are start point, last three are vector.
    # pivot specifies the part of the arrow that is at the grid point, 1st xyzs
    #   passed in. Arrow rotates about this point. Default is 'tip', regardless
    #   of what API says. Arrowheads end at first set of xyzs passed in. Other
    #   option is 'tail' or 'middle'.
    ax.quiver(UVW[0, :],
              UVW[1, :],
              UVW[2, :],
              -UVW[0, :],
              -UVW[1, :],
              -UVW[2, :],
              color=color,
              length=0.4,
              arrow_length_ratio=0.5,
              pivot='tail')

    # 1, 4 go together well, dark blue, light green
    # 1, 3 okay too, dark blue, cyan
    # 1, 5 good too, dark blue, yellow
    color = mpl_color(4, 8, colormap_name=cm_name)
    ax.scatter(UVW[0, :], UVW[1, :], UVW[2, :], c=color)

    ax.set_title('Random poses')
    ax.set_aspect(1)
    ax.set_xlim(-1, 1)
    ax.set_ylim(-1, 1)
    ax.set_zlim(-1, 1)

    fig2.tight_layout()
    if BLACK_BG:
        black_3d_background(ax)
        black_background(ax)

    single_out_base = out_name + '_single_black'

    single_out_name = single_out_base + '.eps'
    fig2.savefig(single_out_name,
                 bbox_inches='tight',
                 facecolor=fig2.get_facecolor(),
                 edgecolor='none',
                 transparent=True)
    print('Written plot to %s' % single_out_name)

    single_out_name = single_out_base + '.png'
    fig2.savefig(single_out_name,
                 bbox_inches='tight',
                 facecolor=fig2.get_facecolor(),
                 edgecolor='none',
                 transparent=True)
    print('Written plot to %s' % single_out_name)

    plt.show()
def plot_bars(obj_ids, obj_errs, obj_names_ordered, lbls_by_value, out_name):

    truetype()

    cm_name = custom_colormap_neon()

    # 2 (light sky blue) and 7 (noen orange) look good
    color = mpl_color(2, 8, colormap_name=cm_name)

    _, title_hdl = plot_line(obj_ids,
                             obj_errs,
                             'Per-Object-Class Errors',
                             'Object',
                             'Error',
                             out_name='',
                             color=color,
                             lbl='',
                             style='bar',
                             dots=False,
                             grid=True,
                             do_save=False,
                             do_show=False,
                             return_title_hdl=True)

    black_background(title_hdl=title_hdl)

    # Limit ticks to object IDs, not the extra columns on left and right
    mpl_diagonal_xticks(plt.gca(), obj_ids, obj_names_ordered, rot_degs=0)

    ## Plot a horizontal line across, at average per-class error

    # Dashed line
    # API https://matplotlib.org/api/_as_gen/matplotlib.axes.Axes.axhline.html
    mean_err = np.mean(obj_errs)
    plt.gca().axhline(y=mean_err,
                      linewidth=1,
                      color='w',
                      linestyle='--',
                      label='Mean per-class')
    mean_err_obj = plt.text(0, mean_err + 0.01, '%.3f' % mean_err, color='w')

    if len(lbls_by_value) > 0:
        smaller_proportion = np.min(lbls_by_value) / float(
            np.sum(lbls_by_value))
        plt.gca().axhline(y=smaller_proportion,
                          linewidth=1,
                          color='r',
                          linestyle='--',
                          label='Labels portion')
        plt.text(0,
                 smaller_proportion + 0.01,
                 '%.3f' % smaller_proportion,
                 color='r')

        # Positive is good, negative is bad
        mean_err_obj.set_text (mean_err_obj.get_text () + \
          ' (%.3f lower)' % (smaller_proportion - mean_err))

    legend_hdl = plt.legend()
    black_legend(legend_hdl)

    # y-axis is percentage
    plt.gca().set_ylim([0.0, 1.0])

    ## Plot

    plt.savefig(out_name,
                bbox_inches='tight',
                facecolor=plt.gcf().get_facecolor(),
                edgecolor='none',
                transparent=True)
    print('%sWritten plot to %s%s' % (ansi.OKCYAN, out_name, ansi.ENDC))

    plt.show()
def draw_confusion_matrix_per_sample(unsorted_idx,
                                     distance_matrix,
                                     ticks=[],
                                     img_name='',
                                     title_prefix='',
                                     draw_title=True,
                                     draw_xylbls=True,
                                     draw_xticks=True,
                                     draw_yticks=True,
                                     fontsize=10,
                                     bg_color='white',
                                     cmap_name='jet_r'):

    # Matrix is n x n
    nSamples = np.shape(unsorted_idx)[0]

    dists_sorted = np.zeros(np.shape(distance_matrix))

    for i in range(0, nSamples):

        # Sort the ith row of sample IDs. Save the indexes from sorting.
        # Ref: http://docs.scipy.org/doc/numpy/reference/generated/numpy.argsort.html
        sorted_idx_rowi = np.argsort(unsorted_idx[i, :])

        # Take ith row of distance matrix, index it using the sorted indices
        dists_sorted[i, :] = distance_matrix[i, :][sorted_idx_rowi]

    print('')
    print ('Max distance: %f. Min distance: %f. Mean distance: %f' % \
      (np.max (dists_sorted), np.min (dists_sorted), np.mean (dists_sorted)))

    print('Confusion matrix:')
    print(dists_sorted)

    # Show confusion matrix in a separate window
    # Using a figure makes colorbar same length as main plot!
    fig = plt.figure()
    plt.matshow(dists_sorted, fignum=fig.number)
    if draw_title:
        if ticks:
            plt.title(title_prefix + 'Confusion matrix', y=1.3)
        else:
            plt.title(title_prefix + 'Confusion matrix')
    # Use jet_r. Reverse colors, so that small dists are hot, far dists are cold.
    #   http://stackoverflow.com/questions/3279560/invert-colormap-in-matplotlib
    # Save orig cmap so can set it back for future plots
    #orig_cmap = plt.get_cmap ()
    plt.set_cmap(cmap_name)
    colorbar = plt.colorbar()

    if draw_xylbls:
        plt.ylabel('Objects')
        plt.xlabel('Objects')

    ax = plt.gca()

    # Display class names on the axes ticks
    if draw_xticks or draw_yticks:
        # Custom ticks passed in
        if len(ticks) > 0:
            draw_classname_ticks(dists_sorted,
                                 ticks,
                                 draw_x=draw_xticks,
                                 draw_y=draw_yticks,
                                 fontsize=fontsize)

        # Automatically generate numerical ticks, every 20 units
        else:
            units_per_tick = 20.0

            if draw_xticks:
                # You can adjust these to make as frequent or as rare ticks as you want
                plt.xticks(np.arange(0, nSamples, units_per_tick))

            if draw_yticks:
                plt.yticks(np.arange(0, nSamples, units_per_tick))

            #print (nSamples)
            #print (np.arange (0, nSamples, units_per_tick))

    if not draw_xticks:
        # http://stackoverflow.com/questions/2176424/hiding-axis-text-in-matplotlib-plots
        #ax.get_xaxis ().set_visible (False)
        ax.get_xaxis().set_ticks([])

    if not draw_yticks:
        #ax.get_yaxis ().set_visible (False)
        ax.get_yaxis().set_ticks([])

    for tick in ax.xaxis.get_major_ticks():
        # http://stackoverflow.com/questions/6390393/matplotlib-make-tick-labels-font-size-smaller
        tick.label.set_fontsize(10)
        # specify integer or one of preset strings, e.g.
        #tick.label.set_fontsize('x-small')
        #tick.label.set_rotation('vertical')

    # Set background color
    if bg_color == 'black':
        black_background()
        black_colorbar(colorbar)

    if img_name:
        # Ref savefig() black background: https://stackoverflow.com/questions/4804005/matplotlib-figure-facecolor-background-color
        plt.savefig(img_name,
                    bbox_inches='tight',
                    facecolor=fig.get_facecolor(),
                    edgecolor='none',
                    transparent=True)
        print('Plot saved to %s' % img_name)

    plt.show()
def draw_confusion_matrix(true_lbls,
                          predicted_lbls,
                          ticks=[],
                          img_name='',
                          title_prefix='',
                          draw_title=True,
                          raw=False,
                          bg_color='white'):

    # Compute confusion matrix
    # Make sure you keep order the same. In (true, predicted) order, the y-axis
    #   is true label, the x-axis is predicted. If you swap these, then the
    #   plot's x- and y-labels need to swap as well.
    # Ref: http://scikit-learn.org/stable/modules/generated/sklearn.metrics.confusion_matrix.html
    cm = confusion_matrix(true_lbls, predicted_lbls)
    print('Confusion matrix:')
    print(cm)

    # Do percentages, instead of raw numbers that are returned by default
    if not raw:
        # Sum each row to get total number of objects in each category (y-axis
        #   label of confusion mat is true labels, so sum rows, not columns).
        #   Need floating point so cm divided by this array gives floats, not ints!
        nTtl = np.sum(cm, axis=1).astype(np.float32)

        # Reshape to column vector, so element-wise divide would divide each row of
        #   conf mat by total object sum in that row.
        nTtl = nTtl.reshape((np.size(nTtl), 1))

        # Element-wise divide each row of confusion matrix by the total # objects
        cm = np.divide(cm, nTtl)
        #print (cm)

    # Show confusion matrix in a separate window
    # Using a figure makes colorbar same length as main plot!
    fig = plt.figure()
    plt.matshow(cm, fignum=fig.number)
    plt.set_cmap('jet')
    colorbar = plt.colorbar()
    if draw_title:
        # Ref move title up: http://stackoverflow.com/questions/12750355/python-matplotlib-figure-title-overlaps-axes-label-when-using-twiny
        plt.title(title_prefix + 'Confusion matrix', y=1.2)
    plt.ylabel('True label')
    plt.xlabel('Predicted label')

    # Display class names on the axes ticks
    if ticks:
        draw_classname_ticks(cm, ticks)

    # Set background color
    if bg_color == 'black':
        black_background()
        black_colorbar(colorbar)

    if img_name:
        # Ref savefig() black background: https://stackoverflow.com/questions/4804005/matplotlib-figure-facecolor-background-color
        plt.savefig(img_name,
                    bbox_inches='tight',
                    facecolor=fig.get_facecolor(),
                    edgecolor='none',
                    transparent=True)
        print('Plot saved to %s' % img_name)

    plt.show()
def save_individual_subplots(figs,
                             ylbls,
                             ylbl_to_save,
                             nRows,
                             nCols,
                             scale_xmin=1.1,
                             scale_x=1.2,
                             scale_y=1.15,
                             ndims=3,
                             show=True,
                             bg_color='white',
                             tick_rot=0):

    x_expand_idx = []
    y_expand_idx = []
    # Expand horizontal space for plots on left column
    for i in range(0, nRows):
        # Subplots on left have indices 0*n, 1*n, 2*n, ..., (n-1)*n. Add margin on
        #   x on left.
        x_expand_idx.append(i * nRows)

    # Expand vertical space for plots on botom row
    for i in range(0, nCols):
        # Subplots at bottom have indices (n-1)*n+0, (n-1)*n+1, (n-1)*n+2, ...,
        #   (n-1)*n+(n-1). Add margin on y at bottom.
        y_expand_idx.append((nRows - 1) * nCols + i)

    # Constants for scaling bbox, tuned for ticks and labels with fontsize 6
    SCALE_XMIN = scale_xmin
    SCALE_YMIN = 1.1
    SCALE_X = scale_x
    SCALE_Y = scale_y

    # Find the figure that matches the desired ylbl
    for f_i in range(0, len(ylbls)):

        if ylbls[f_i] != ylbl_to_save:
            continue

        # Found the figure with desired ylbl
        else:
            imgpath = get_img_path('hists')
            imgname = os.path.join(
                imgpath, 'triangle_' + str(ndims) + 'Dhist_' + ylbls[f_i])

            axes = figs[f_i].get_axes()

            for sp_i in range(0, len(axes)):
                curr_imgname = imgname + ('_%02d' % sp_i) + '.eps'

                # Ref save a subplot (axis) in a figure to file:
                #   http://stackoverflow.com/questions/4325733/save-a-subplot-in-matplotlib
                extent = axes[sp_i].get_window_extent().transformed(
                    figs[f_i].dpi_scale_trans.inverted())

                # Default. Need 1.05 to show entire plot, 1 doesn't show whole thing!
                #   1.3 shows axes labels (font size 6) perfectly, for x or y
                bbox_inches = extent.expanded(SCALE_XMIN, SCALE_YMIN)
                # Record original x y position of box, before expanding
                #   http://matplotlib.org/devel/transformations.html
                xmax_orig = bbox_inches.xmax
                ymax_orig = bbox_inches.ymax

                # expanded() works outwards from center only, can't make box fixed
                #   in one corner. So have to manually move box back after expand.
                # extent is a matplotlib.transforms.BboxBase type
                #   Ref http://matplotlib.org/devel/transformations.html
                # Subplot on lower-left needs to expand in both x and y
                if sp_i in x_expand_idx and sp_i in y_expand_idx:
                    bbox_inches = extent.expanded(SCALE_X, SCALE_Y)
                # Only expand x, on left
                elif sp_i in x_expand_idx:
                    # x should be same as above, so last row is same width as prev rows
                    bbox_inches = extent.expanded(SCALE_X, SCALE_YMIN)
                # Only expand y, on bottom
                elif sp_i in y_expand_idx:
                    # Need extra y to show bottom x label, need extra x to show left-most
                    #   tick.
                    bbox_inches = extent.expanded(SCALE_XMIN, SCALE_Y)

                # Shift content in bbox to sit at upper-right of the expanded bbox
                xmax_new = bbox_inches.xmax
                ymax_new = bbox_inches.ymax
                bbox_inches = bbox_inches.translated(xmax_orig - xmax_new,
                                                     ymax_orig - ymax_new)

                if bg_color == 'black':
                    black_background(axes[sp_i])

                # Ref savefig() black background: https://stackoverflow.com/questions/4804005/matplotlib-figure-facecolor-background-color
                figs[f_i].savefig(curr_imgname,
                                  bbox_inches=bbox_inches,
                                  facecolor=bg_color,
                                  edgecolor='none',
                                  transparent=True)

                print ('Individual axes %d in dimension %s saved to %s' % ( \
                  sp_i, ylbl_to_save, curr_imgname))

            break