Exemplo n.º 1
0
def main():
    parser = argparse.ArgumentParser(
        formatter_class=argparse.RawDescriptionHelpFormatter,
        description='GUI for sorting filenames')
    parser.add_argument("stack", type=str, help="stack name")
    args = parser.parse_args()
    stack = args.stack
    sqlController = SqlController()
    sections = list(sqlController.get_valid_sections(stack))
    # Queued transformations keeps track of each transformation the user wants to perform
    #   on ALL the images. The transformations are applied to the large "raw" files after
    #   the user clicks "done orienting"

    if len(sections) > 0:
        global app
        app = QApplication(sys.argv)
        global ex
        ex = init_GUI(stack)
        # Run GUI as usual
        ex.show()
        # Simulate a user's keypress because otherwise the autozoom is weird
        ex.keyPressEvent(91)
        sys.exit(app.exec_())
    else:
        print('There are no sections to work with.')
Exemplo n.º 2
0
def create_thumbnails(stack):
    ## Downsample and normalize images in the "_raw" folder

    raw_folder = os.path.join(ROOT_DIR, stack, 'tif')
    sections = SqlController.get_valid_sections(stack)
    for img_name in os.listdir(raw_folder):
        input_fp = os.path.join(raw_folder, img_name)
        output_fp = os.path.join(ROOT_DIR, stack, 'preps', 'thumbnail', img_name)

        # Create thumbnails
        execute_command("convert \"" + input_fp + "\" -resize 3.125% -auto-level -normalize \
                        -compress lzw \"" + output_fp + "\"")
Exemplo n.º 3
0
def main():
    parser = argparse.ArgumentParser(
        formatter_class=argparse.RawDescriptionHelpFormatter,
        description='GUI for sorting filenames')
    parser.add_argument("stack", type=str, help="stack name")
    args = parser.parse_args()
    stack = args.stack
    sqlController = SqlController()
    sections = list(sqlController.get_valid_sections(stack))

    if len(sections) > 0:
        global app
        app = QApplication(sys.argv)
        global ex
        ex = init_GUI(stack)
        # Run GUI as usual
        ex.show()
        sys.exit(app.exec_())
    else:
        print('There are no sections to work with.')
Exemplo n.º 4
0
def create_anchor_file(stack, anchor_fn='auto'):
    """
    This method gets the current entry from valid sections and writes it to a file
    Args:
        stack: animal
        anchor_fn: auto
    Returns: a string of the file name
    """
    if anchor_fn == 'auto':
        sqlController = SqlController()
        valid_sections = sqlController.get_valid_sections(stack)
        valid_section_keys = sorted(list(valid_sections))
        curr_section_index = len(valid_section_keys) // 2
        anchor_fn = valid_sections[
            valid_section_keys[curr_section_index]]['destination']

    # First designate an anchor to use
    anchor_text_fp = os.path.join(fileLocationManager.brain_info, 'anchor.txt')

    f = open(anchor_text_fp, "w")
    f.write(anchor_fn)
    f.close()
    # Returns the chosen anchor filename just in case it is being suto-selected
    return anchor_fn
Exemplo n.º 5
0
class PreprocessGUI(QMainWindow, Ui_PreprocessGui):

    def __init__(self, parent=None, stack=None, tb_fmt='png', tb_res='down32', tb_version=None):
        """
        Initialization of preprocessing tool.
        """
        QMainWindow.__init__(self, parent)
        self.setupUi(self)

        self.stack = stack
        self.fileLocationManager = FileLocationManager(self.stack)
        self.sqlController = SqlController()

        self.currently_showing = 'original'
        self.tb_fmt = tb_fmt
        self.tb_res = tb_res
        self.tb_version = tb_version
        self.stack_data_dir = self.fileLocationManager.thumbnail_prep

        self.show_valid_only = True

        #_, self.section_to_filename = DataManager.load_sorted_filenames(self.stack)
        #self.filename_to_accept_decision = {fn: True for sec, fn in self.section_to_filename.iteritems() if not is_invalid(fn)}
        self.section_to_filename = self.sqlController.get_valid_sections(self.stack)
        # self.sorted_filenames = [f for s,f in sorted(section_to_filename.items())]

        self.placeholder_qimage = QImage(800, 500, QImage.Format_RGB888)
        painter = QPainter(self.placeholder_qimage)
        painter.fillRect(self.placeholder_qimage.rect(), Qt.yellow);
        painter.drawText(self.placeholder_qimage.rect(), Qt.AlignCenter | Qt.AlignVCenter, "Placeholder");

        self.rescan_qimage = QImage(800, 500, QImage.Format_RGB888)
        painter = QPainter(self.rescan_qimage)
        painter.fillRect(self.rescan_qimage.rect(), Qt.green);
        painter.drawText(self.rescan_qimage.rect(), Qt.AlignCenter | Qt.AlignVCenter, "Rescan");

        self.nonexisting_qimage = QImage(800, 500, QImage.Format_RGB888)
        painter = QPainter(self.nonexisting_qimage)
        painter.fillRect(self.nonexisting_qimage.rect(), Qt.white);
        painter.drawText(self.nonexisting_qimage.rect(), Qt.AlignCenter | Qt.AlignVCenter, "Nonexisting");

        self.first_section = None
        self.last_section = None

        self.sorted_sections_gscene = SimpleGraphicsScene3(id='sorted', gview=self.sorted_sections_gview)
        self.sorted_sections_gview.setScene(self.sorted_sections_gscene)

        self.sorted_sections_gscene.active_image_updated.connect(self.sorted_sections_image_updated)
        self.sorted_sections_gscene.first_section_set.connect(self.set_first_section)
        self.sorted_sections_gscene.last_section_set.connect(self.set_last_section)
        self.sorted_sections_gscene.anchor_set.connect(self.set_anchor)
        self.sorted_sections_gscene.move_down_requested.connect(self.move_down)
        self.sorted_sections_gscene.move_up_requested.connect(self.move_up)

        #######################

        self.installEventFilter(self)

        self.comboBox_show.activated.connect(self.show_option_changed)
        # self.button_sort.clicked.connect(self.sort)
        self.button_load_sorted_filenames.clicked.connect(self.load_sorted_filenames)
        # self.button_save_sorted_filenames.clicked.connect(self.save_sorted_filenames)
        # self.button_confirm_alignment.clicked.connect(self.compose)
        self.button_edit_transform.clicked.connect(self.edit_transform)
        # self.button_crop.clicked.connect(self.crop)
        self.button_save_crop.clicked.connect(self.save_crop)
        self.button_load_crop.clicked.connect(self.load_crop)
        self.button_update_order.clicked.connect(self.update_sorted_sections_gscene_from_sorted_filenames)
        self.button_toggle_show_hide_invalid.clicked.connect(self.toggle_show_hide_invalid)

        ################################

        self.placeholders = set([])
        self.rescans = set([])

        pp_fp = os.path.join(self.fileLocationManager.brain_info, 'problematic_pairs.txt')
        if os.path.exists(pp_fp):
            sys.stderr.write("Loaded problematic pairs.\n")
            with open(pp_fp, 'r') as f:
                self.problematic_pairs = [tuple(line.split()) for line in f.readlines()]
        else:
            sys.stderr.write("Did not find problematic pairs.\n")
            self.problematic_pairs = []

    def toggle_show_hide_invalid(self):
        self.show_valid_only = not self.show_valid_only
        print(self.show_valid_only)
        self.button_toggle_show_hide_invalid.setText('Show both valid and invalid' if self.show_valid_only else 'Show valid only')
        self.update_sorted_sections_gscene_from_sorted_filenames()

    def move_down(self):

        curr_fn = self.sorted_sections_gscene.active_section
        next_fn = self.sorted_sections_gscene.data_feeder.sections[self.sorted_sections_gscene.active_i + 1]

        filename_to_section = self.section_to_filename

        curr_fn_section = filename_to_section[curr_fn]
        next_fn_section = filename_to_section[next_fn]

        self.section_to_filename[curr_fn_section] = next_fn
        self.section_to_filename[next_fn_section] = curr_fn

        self.update_sorted_sections_gscene_from_sorted_filenames()
        self.sorted_sections_gscene.set_active_section(curr_fn)

    def move_up(self):

        curr_fn = self.sorted_sections_gscene.active_section
        prev_fn = self.sorted_sections_gscene.data_feeder.sections[self.sorted_sections_gscene.active_i - 1]

        filename_to_section = self.section_to_filename

        curr_fn_section = filename_to_section[curr_fn]
        prev_fn_section = filename_to_section[prev_fn]

        self.section_to_filename[curr_fn_section] = prev_fn
        self.section_to_filename[prev_fn_section] = curr_fn

        self.update_sorted_sections_gscene_from_sorted_filenames()
        self.sorted_sections_gscene.set_active_section(curr_fn)

    def load_crop(self):
        """
        Load crop box.
        """
        self.set_show_option('aligned')
        cropbox_fp = DataManager.get_cropbox_filename(stack=self.stack, anchor_fn=self.anchor_fn)
        with open(cropbox_fp, 'r') as f:
            ul_x, lr_x, ul_y, lr_y, first_section, last_section = map(int, f.readline().split())
            self.first_section = self.section_to_filename[first_section]
            self.last_section = self.section_to_filename[last_section]
            self.sorted_sections_gscene.set_box(ul_x, lr_x, ul_y, lr_y)
            print(ul_x, lr_x, ul_y, lr_y, self.first_section, self.last_section)

    def save_crop(self):
        ul_pos = self.sorted_sections_gscene.corners['ul'].scenePos()
        lr_pos = self.sorted_sections_gscene.corners['lr'].scenePos()
        ul_x = int(ul_pos.x())
        ul_y = int(ul_pos.y())
        lr_x = int(lr_pos.x())
        lr_y = int(lr_pos.y())

        # If not set yet.
        if ul_x == 100 and ul_y == 100 and lr_x == 200 and lr_y == 200:
            return

        cropbox_fp = DataManager.get_cropbox_filename(stack=self.stack, anchor_fn=self.anchor_fn)

        filename_to_section = invert_section_to_filename_mapping(self.section_to_filename)
        with open(cropbox_fp, 'w') as f:
            f.write('%d %d %d %d %d %d' % (ul_x, lr_x, ul_y, lr_y, filename_to_section[self.first_section], filename_to_section[self.last_section]))

        upload_to_s3(cropbox_fp)

    def crop(self):
        pass
        ## Note that in cropbox, xmax, ymax are not included, so w = xmax-xmin, instead of xmax-xmin+1

        # self.save_crop()
        #
        # ul_pos = self.sorted_sections_gscene.corners['ul'].scenePos()
        # lr_pos = self.sorted_sections_gscene.corners['lr'].scenePos()
        # ul_x = int(ul_pos.x())
        # ul_y = int(ul_pos.y())
        # lr_x = int(lr_pos.x())
        # lr_y = int(lr_pos.y())
        #
        # if self.stack in all_nissl_stacks:
        #     pad_bg_color = 'white'
        # elif self.stack in all_ntb_stacks:
        #     pad_bg_color = 'black'
        # elif self.stack in all_alt_nissl_ntb_stacks or self.stack in all_alt_nissl_tracing_stacks:
        #     pad_bg_color = 'auto'
        #
        # self.web_service.convert_to_request('crop', stack=self.stack, x=ul_x, y=ul_y, w=lr_x+1-ul_x, h=lr_y+1-ul_y,
        #                                     f=self.first_section, l=self.last_section, anchor_fn=self.anchor_fn,
        #                                     filenames=self.get_valid_sorted_filenames(),
        #                                     first_fn=self.sorted_filenames[self.first_section-1],
        #                                     last_fn=self.sorted_filenames[self.last_section-1],
        #                                     pad_bg_color=pad_bg_color)

    ##################
    # Edit Alignment #
    ##################

    def edit_transform(self):

        sys.stderr.write('Loading Edit Transform GUI...\n')
        self.statusBar().showMessage('Loading Edit Transform GUI...')

        self.alignment_ui = Ui_AlignmentGui()
        self.alignment_gui = QDialog(self)
        self.alignment_gui.setWindowTitle("Edit transform between adjacent sections")
        self.alignment_ui.setupUi(self.alignment_gui)

        self.alignment_ui.button_anchor.clicked.connect(self.add_anchor_pair_clicked)
        self.alignment_ui.button_align.clicked.connect(self.align_using_elastix)
        self.alignment_ui.button_compute.clicked.connect(self.compute_custom_transform)

        param_fps = os.listdir(DataManager.get_elastix_parameters_dir())
        all_parameter_setting_names = ['_'.join(pf[:-4].split('_')[1:]) for pf in param_fps]
        self.alignment_ui.comboBox_parameters.addItems(all_parameter_setting_names)

        section_filenames = self.get_sorted_filenames(valid_only=self.show_valid_only)

        # Initialize gscene "current"
        self.curr_gscene = SimpleGraphicsScene4(id='current', gview=self.alignment_ui.curr_gview)
        self.alignment_ui.curr_gview.setScene(self.curr_gscene)
        self.curr_gscene.set_data_feeder(self.ordered_images_feeder)
        self.curr_gscene.set_active_i(1)
        self.curr_gscene.active_image_updated.connect(self.current_section_image_changed)
        self.curr_gscene.anchor_point_added.connect(self.anchor_point_added)

        # Initialize gscene "previous"
        self.prev_gscene = SimpleGraphicsScene4(id='previous', gview=self.alignment_ui.prev_gview)
        self.alignment_ui.prev_gview.setScene(self.prev_gscene)
        self.prev_gscene.set_data_feeder(self.ordered_images_feeder)
        self.prev_gscene.set_active_i(0)
        self.prev_gscene.active_image_updated.connect(self.previous_section_image_changed)
        self.prev_gscene.anchor_point_added.connect(self.anchor_point_added)

        # Initialize gscene "overlay"
        self.overlay_gscene = MultiplePixmapsGraphicsScene(id='overlay', pixmap_labels=['moving', 'fixed'], gview=self.alignment_ui.aligned_gview)
        self.alignment_ui.aligned_gview.setScene(self.overlay_gscene)

        self.transformed_images_feeder = ImageDataFeeder_v2('transformed image feeder', stack=self.stack, sections=section_filenames, resolution=self.tb_res)
        self.update_transformed_images_feeder()
        self.overlay_gscene.set_data_feeder(self.transformed_images_feeder, 'moving')

        self.overlay_gscene.set_data_feeder(self.ordered_images_feeder, 'fixed')
        self.overlay_gscene.set_active_indices({'moving': 1, 'fixed': 0})
        self.overlay_gscene.set_opacity('moving', .3)
        self.overlay_gscene.set_opacity('fixed', .3)
        self.overlay_gscene.active_image_updated.connect(self.overlay_image_changed)

        self.alignment_gui.show()

    def update_transformed_images_feeder(self):

        section_filenames = self.get_sorted_filenames(valid_only=self.show_valid_only)

        transformed_image_filenames = []
        for i in range(1, len(section_filenames)):
            fp = DataManager.load_image_filepath_warped_to_adjacent_section(stack=self.stack, moving_fn=section_filenames[i], fixed_fn=section_filenames[i-1])
            transformed_image_filenames.append(fp)

        self.transformed_images_feeder.set_images(labels=section_filenames[1:], filenames=transformed_image_filenames, resolution=self.tb_res, load_with_cv2=True)

    def align_using_elastix(self):
        selected_elastix_parameter_name = str(self.alignment_ui.comboBox_parameters.currentText())
        param_fn = os.path.join(UTILITY_DIR, 'preprocess', 'parameters', 'Parameters_' + selected_elastix_parameter_name + '.txt')

        curr_fn = self.curr_gscene.active_section
        prev_fn = self.prev_gscene.active_section
        out_dir = os.path.join(self.stack_data_dir, self.stack + '_custom_transforms', curr_fn + '_to_' + prev_fn)

        curr_fp = DataManager.get_image_filepath_v2(stack=self.stack, prep_id=None, fn=curr_fn, resol=self.tb_res, version=self.tb_version)
        prev_fp = DataManager.get_image_filepath_v2(stack=self.stack, prep_id=None, fn=prev_fn, resol=self.tb_res, version=self.tb_version )

        # curr_fp = os.path.join(RAW_DATA_DIR, self.stack, curr_fn + '.' + self.tb_fmt)
        # prev_fp = os.path.join(RAW_DATA_DIR, self.stack, prev_fn + '.' + self.tb_fmt)

        execute_command('rm -rf %(out_dir)s; mkdir -p %(out_dir)s; elastix -f %(fixed_fn)s -m %(moving_fn)s -out %(out_dir)s -p %(param_fn)s' % \
        dict(param_fn=param_fn, out_dir=out_dir, fixed_fn=prev_fp, moving_fn=curr_fp))
        # section_filenames = self.get_sorted_filenames(valid_only=self.show_valid_only)

        self.update_transformed_images_feeder()

    def anchor_point_added(self, index):
        gscene_id = self.sender().id
        if gscene_id == 'current':
            self.current_section_anchor_received = True
            # self.curr_gscene.anchor_points.index
        elif gscene_id == 'previous':
            self.previous_section_anchor_received = True

        if self.current_section_anchor_received and self.previous_section_anchor_received:
            self.curr_gscene.set_mode('idle')
            self.prev_gscene.set_mode('idle')
            self.current_section_anchor_received = False
            self.previous_section_anchor_received = False
            self.alignment_ui.button_anchor.setEnabled(True)

    def compute_custom_transform(self):
        """
        Compute transform based on added control points.
        """

        curr_points = []
        for i, c in enumerate(self.curr_gscene.anchor_circle_items):
            pos = c.scenePos()
            curr_points.append((pos.x(), pos.y()))

        prev_points = []
        for i, c in enumerate(self.prev_gscene.anchor_circle_items):
            pos = c.scenePos()
            prev_points.append((pos.x(), pos.y()))

        print(self.curr_gscene.active_section, np.array(curr_points))
        print(self.prev_gscene.active_section, np.array(prev_points))

        curr_points = np.array(curr_points)
        prev_points = np.array(prev_points)
        curr_centroid = curr_points.mean(axis=0)
        prev_centroid = prev_points.mean(axis=0)
        curr_points0 = curr_points - curr_centroid
        prev_points0 = prev_points - prev_centroid

        H = np.dot(curr_points0.T, prev_points0)
        U, S, VT = np.linalg.svd(H)
        R = np.dot(VT.T, U.T)

        t = -np.dot(R, curr_centroid) + prev_centroid

        print(R, t)

        # Write to custom transform file
        curr_section_fn = self.curr_gscene.active_section
        prev_section_fn = self.prev_gscene.active_section

        custom_tf_dir = os.path.join(self.stack_data_dir, self.stack + '_custom_transforms', curr_section_fn + '_to_' + prev_section_fn)

        execute_command("rm -rf %(out_dir)s; mkdir -p %(out_dir)s" % dict(out_dir=custom_tf_dir))
        custom_tf_fp = os.path.join(custom_tf_dir, '%(curr_fn)s_to_%(prev_fn)s_customTransform.txt' % \
                    dict(curr_fn=curr_section_fn, prev_fn=prev_section_fn))

        with open(custom_tf_fp, 'w') as f:
            f.write('%f %f %f %f %f %f\n' % (R[0,0], R[0,1], t[0], R[1,0], R[1,1], t[1]))

        self.apply_custom_transform()
        self.update_transformed_images_feeder()

    def apply_custom_transform(self):

        # section_filenames = self.get_sorted_filenames(valid_only=self.show_valid_only)

        # curr_section_fn = section_filenames[self.valid_section_indices[self.curr_gscene.active_i]-1]
        # prev_section_fn = section_filenames[self.valid_section_indices[self.prev_gscene.active_i]-1]
        curr_section_fn = self.curr_gscene.active_section
        prev_section_fn = self.prev_gscene.active_section

        custom_tf_fn = os.path.join(self.stack_data_dir, self.stack+'_custom_transforms', curr_section_fn + '_to_' + prev_section_fn, curr_section_fn + '_to_' + prev_section_fn + '_customTransform.txt')
        with open(custom_tf_fn, 'r') as f:
            t11, t12, t13, t21, t22, t23 = map(float, f.readline().split())

        prev_fp = DataManager.get_image_filepath_v2(stack=self.stack, prep_id=None, fn=prev_section_fn, resol=self.tb_res, version=self.tb_version )
        curr_fp = DataManager.get_image_filepath_v2(stack=self.stack, prep_id=None, fn=curr_section_fn, resol=self.tb_res, version=self.tb_version )
        prev_img_w, prev_img_h = identify_shape(prev_fp)

        output_image_fp = os.path.join(self.stack_data_dir, '%(stack)s_custom_transforms/%(curr_fn)s_to_%(prev_fn)s/%(curr_fn)s_alignedTo_%(prev_fn)s.tif' % \
                        dict(stack=self.stack,
                        curr_fn=curr_section_fn,
                        prev_fn=prev_section_fn) )

        execute_command("convert %(curr_fp)s -virtual-pixel background +distort AffineProjection '%(sx)f,%(rx)f,%(ry)f,%(sy)f,%(tx)f,%(ty)f' -crop %(w)sx%(h)s%(x)s%(y)s\! -flatten -compress lzw %(output_fp)s" %\
        dict(curr_fp=curr_fp,
            output_fp=output_image_fp,
            tb_fmt=self.tb_fmt,
            sx=t11,
            sy=t22,
            rx=t21,
            ry=t12,
            tx=t13,
            ty=t23,
            w=prev_img_w,
            h=prev_img_h,
            x='+0',
            y='+0',
            raw_data_dir=RAW_DATA_DIR))

    def add_anchor_pair_clicked(self):
        self.curr_gscene.set_mode('add point')
        self.prev_gscene.set_mode('add point')
        self.current_section_anchor_received = False
        self.previous_section_anchor_received = False
        self.alignment_ui.button_anchor.setEnabled(False)

    def overlay_image_changed(self):
        section_filenames = self.get_sorted_filenames(valid_only=self.show_valid_only)

        curr_section_fn = self.overlay_gscene.active_sections['moving']
        prev_section_fn = self.overlay_gscene.active_sections['fixed']

        print("Problematic pairs:", self.problematic_pairs)

        if (prev_section_fn, curr_section_fn) in self.problematic_pairs:
            self.alignment_ui.label_current_filename.setText('(CHECK)' + str(curr_section_fn))
            self.alignment_ui.label_current_index.setText('(CHECK)' + str(curr_section_fn))
        else:
            self.alignment_ui.label_current_filename.setText(str(curr_section_fn))
            self.alignment_ui.label_current_index.setText(str(curr_section_fn))
        self.curr_gscene.set_active_section(curr_section_fn)
        self.prev_gscene.set_active_section(prev_section_fn)

    def current_section_image_changed(self):
        section_filenames = self.get_sorted_filenames(valid_only=self.show_valid_only)

        curr_section_fn = self.curr_gscene.active_section
        prev_section_fn = section_filenames[section_filenames.index(curr_section_fn) - 1]

        print("Problematic pairs:", self.problematic_pairs)

        if (prev_section_fn, curr_section_fn) in self.problematic_pairs:
            self.alignment_ui.label_current_filename.setText('(CHECK)' + str(curr_section_fn))
            self.alignment_ui.label_current_index.setText('(CHECK)' + str(curr_section_fn))
        else:
            self.alignment_ui.label_current_filename.setText(str(curr_section_fn))
            self.alignment_ui.label_current_index.setText(str(curr_section_fn))
        self.prev_gscene.set_active_section(prev_section_fn)
        self.overlay_gscene.set_active_sections({'moving': curr_section_fn, 'fixed': prev_section_fn})

    def previous_section_image_changed(self):
        section_filenames = self.get_sorted_filenames(valid_only=self.show_valid_only)

        prev_section_fn = self.prev_gscene.active_section
        curr_section_fn = section_filenames[section_filenames.index(prev_section_fn) + 1]

        self.alignment_ui.label_previous_filename.setText(prev_section_fn)
        self.alignment_ui.label_previous_index.setText(str(prev_section_fn))
        self.curr_gscene.set_active_section(curr_section_fn)
        self.overlay_gscene.set_active_sections({'moving': curr_section_fn, 'fixed': prev_section_fn}, emit_changed_signal=False)

    ########################## END OF EDIT TRANSFORM ######################################3

    def save_everything(self):

        # Dump preprocessing info
        placeholder_indices = [idx+1 for idx, fn in enumerate(self.sorted_filenames) if fn == 'Placeholder']
        placeholder_slide_positions = [(slide_name, pos) for slide_name, x in self.slide_position_to_fn.iteritems() for pos, fn in x.iteritems() if fn == 'Placeholder']
        rescan_indices = [idx+1 for idx, fn in enumerate(self.sorted_filenames) if fn == 'Rescan']
        rescan_slide_positions = [(slide_name, pos) for slide_name, x in self.slide_position_to_fn.iteritems() for pos, fn in x.iteritems() if fn == 'Rescan']

        ul_pos = self.sorted_sections_gscene.corners['ul'].scenePos()
        lr_pos = self.sorted_sections_gscene.corners['lr'].scenePos()
        ul_x = int(ul_pos.x())
        ul_y = int(ul_pos.y())
        lr_x = int(lr_pos.x())
        lr_y = int(lr_pos.y())

        info = {'placeholder_indices': placeholder_indices,
        'placeholder_slide_positions': placeholder_slide_positions,
        'rescan_indices': rescan_indices,
        'rescan_slide_positions': rescan_slide_positions,
        'sorted_filenames': self.sorted_filenames,
        'slide_position_to_fn': self.slide_position_to_fn,
        'first_section': self.first_section,
        'last_section': self.last_section,
        'anchor_fn': self.anchor_fn,
        # 'bbox': (ul_x, lr_x, ul_y, lr_y) #xmin,xmax,ymin,ymax
        'bbox': (ul_x, ul_y, lr_x+1-ul_x, lr_y+1-ul_y) #xmin,ymin,w,h
        }

        from datetime import datetime
        timestamp = datetime.now().strftime("%m%d%Y%H%M%S")
        pickle.dump(info, open(self.stack_data_dir + '/%(stack)s_preprocessInfo_%(timestamp)s.pkl' % {'stack': self.stack, 'timestamp':timestamp}, 'w'))

        execute_command('cd %(stack_data_dir)s && rm -f %(stack)s_preprocessInfo.pkl && ln -s %(stack)s_preprocessInfo_%(timestamp)s.pkl %(stack)s_preprocessInfo.pkl' % {'stack': self.stack, 'timestamp':timestamp, 'stack_data_dir':self.stack_data_dir})

        self.save_crop()
        self.save_sorted_filenames()
        self.save()

    def update_sorted_sections_gscene_from_sorted_filenames(self):

        if not hasattr(self, 'currently_showing'):
            self.currently_showing = 'original'

        if not hasattr(self, 'anchor_fn'):
            anchor_fp = DataManager.get_anchor_filename_filename(self.stack)
            if os.path.exists(anchor_fp):
                with open(anchor_fp) as f:
                    self.set_anchor(f.readline().strip())
            else:
                imageNames_to_load = self.get_sorted_filenames(valid_only=self.show_valid_only)
                shapes = \
                    [identify_shape(DataManager.get_image_filepath_v2(stack=self.stack, fn=fn, prep_id=None, version=self.tb_version, resol=self.tb_res))
                    for fn in imageNames_to_load]
                largest_idx = np.argmax([h*w for h, w in shapes])
                print('largest section is ', imageNames_to_load[largest_idx])
                self.set_anchor(imageNames_to_load[largest_idx])
                print(imageNames_to_load[largest_idx])

        if self.currently_showing == 'original':

            imageNames_to_load = self.get_sorted_filenames(valid_only=self.show_valid_only)

            if not hasattr(self, 'ordered_images_feeder') or self.ordered_images_feeder is None:
                self.ordered_images_feeder = ImageDataFeeder_v2('ordered image feeder', stack=self.stack,
                                            sections=imageNames_to_load, resolution=self.tb_res, use_thread=False, auto_load=False)

                self.ordered_images_feeder.set_images(labels=imageNames_to_load,
                                                filenames=[DataManager.get_image_filepath_v2(stack=self.stack, fn=fn, prep_id=None,
                                                version=self.tb_version, resol=self.tb_res)
                                                                            for fn in imageNames_to_load],
                                                resolution=self.tb_res, load_with_cv2=False)
                self.ordered_images_feeder.set_images(labels=['Placeholder'],
                                                filenames=[self.placeholder_qimage],
                                                resolution=self.tb_res, load_with_cv2=False)
            else:
                self.ordered_images_feeder.set_sections(imageNames_to_load)

            self.sorted_sections_gscene.set_data_feeder(self.ordered_images_feeder)

            if self.sorted_sections_gscene.active_i is not None:
                active_i = self.sorted_sections_gscene.active_i
            else:
                active_i = 1
            self.sorted_sections_gscene.set_active_i(active_i)

        elif self.currently_showing == 'aligned':

            imageNames_to_load = self.get_sorted_filenames(valid_only=self.show_valid_only)

            if not hasattr(self, 'aligned_images_feeder') or self.aligned_images_feeder is None:
                self.aligned_images_feeder = ImageDataFeeder_v2('aligned image feeder', stack=self.stack,
                                    sections=imageNames_to_load, resolution=self.tb_res, use_thread=False, auto_load=False)
                self.aligned_images_feeder.set_images(labels=imageNames_to_load,
                                                filenames=[DataManager.get_image_filepath_v2(stack=self.stack, fn=fn, prep_id='alignedPadded', version=self.tb_version, resol=self.tb_res)
                                                                            for fn in imageNames_to_load],
                                                resolution=self.tb_res, load_with_cv2=False)

                self.aligned_images_feeder.set_images(labels=['Placeholder'],
                                                filenames=[self.placeholder_qimage],
                                                resolution=self.tb_res, load_with_cv2=False)
            else:
                self.aligned_images_feeder.set_sections(imageNames_to_load)

            self.sorted_sections_gscene.set_data_feeder(self.aligned_images_feeder)

            if self.sorted_sections_gscene.active_i is not None:
                active_i = self.sorted_sections_gscene.active_i
            else:
                active_i = 1
            self.sorted_sections_gscene.set_active_i(active_i)

        # elif self.currently_showing == 'mask_contour':
        #
        #     self.maskContourViz_images_feeder = ImageDataFeeder_v2('mask contoured image feeder', stack=self.stack,
        #                                         sections=self.valid_section_indices, resolution=self.tb_res)
        #     self.maskContourViz_images_dir = self.stack_data_dir + '/%(stack)s_maskContourViz_unsorted' % {'stack': self.stack}
        #     maskContourViz_image_filenames = [os.path.join(self.maskContourViz_images_dir, '%(fn)s_mask_contour_viz.tif' % {'fn': fn})
        #                                 for fn in self.valid_section_filenames]
        #
        #     self.maskContourViz_images_feeder.set_images(self.valid_section_indices, maskContourViz_image_filenames, resolution=self.tb_res, load_with_cv2=False)
        #     # self.maskContourViz_images_feeder.set_resolution(self.tb_res)
        #
        #     active_i = self.sorted_sections_gscene.active_i
        #     self.sorted_sections_gscene.set_data_feeder(self.maskContourViz_images_feeder)
        #     self.sorted_sections_gscene.set_active_i(active_i)


    def save_sorted_filenames(self):

        sorted_filenames = self.get_sorted_filenames(valid_only=False)

        out_sorted_image_names_fp = DataManager.get_sorted_filenames_filename(stack=self.stack)
        with open(out_sorted_image_names_fp, 'w') as f:
            for i, fn in enumerate(sorted_filenames):
                f.write('%s %03d\n' % (fn, i+1)) # index starts from 1

        upload_to_s3(out_sorted_image_names_fp)

        sys.stderr.write('Sorted filename list saved.\n')
        self.statusBar().showMessage('Sorted filename list saved.')


    def load_sorted_filenames(self):

        # filename_to_section, section_to_filename = DataManager.load_sorted_filenames(self.stack)
        # self.sorted_filenames = [f for s,f in sorted(section_to_filename.items())]

        sys.stderr.write('Sorted filename list is loaded.\n')
        self.statusBar().showMessage('Sorted filename list is loaded.')

        # self.fn_to_slide_position = {fn: (slide, pos) for slide, pos_to_fn in self.slide_position_to_fn.iteritems() for pos, fn in pos_to_fn.iteritems()}
        # self.sorted_slide_positions = [self.fn_to_slide_position[fn] for fn in self.sorted_filenames]

        self.update_sorted_sections_gscene_from_sorted_filenames()

    def load_slide_position_map(self):
        fn = self.stack_data_dir + '/%(stack)s_slide_position_to_fn.pkl' % {'stack': self.stack}
        if os.path.exists(fn):
            self.slide_position_to_fn = pickle.load(open(fn, 'r'))
            sys.stderr.write('Slide position to image filename mapping is loaded.\n')
            self.statusBar().showMessage('Slide position to image filename mapping is loaded.')
        else:
            sys.stderr.write('Cannot load slide position to image filename mapping - File does not exists.\n')
            self.statusBar().showMessage('Cannot load slide position to image filename mapping - File does not exists.')

    def save(self):
        pickle.dump(self.slide_position_to_fn, open(self.stack_data_dir + '/%(stack)s_slide_position_to_fn.pkl' % {'stack': self.stack}, 'w') )

    def get_sorted_filenames(self, valid_only=False):
        return [fn for sec, fn in sorted(self.section_to_filename.items())
        if (fn != 'Rescan' and fn != 'Placeholder' and self.filename_to_accept_decision[fn]) or not valid_only]

    def compose(self):
        pass

    def set_first_section(self, fn):
        self.first_section = str(fn)
        self.update_sorted_sections_gscene_label()

    def set_last_section(self, fn):
        self.last_section = str(fn)
        self.update_sorted_sections_gscene_label()

    def set_anchor(self, anchor):
        if isinstance(anchor, int):
            self.anchor_fn = self.sorted_filenames[anchor-1]
        elif isinstance(anchor, str):
            self.anchor_fn = anchor

        with open(os.path.join(THUMBNAIL_DATA_DIR, self.stack, self.stack + '_anchor.txt'), 'w') as f:
            f.write(self.anchor_fn)

        self.update_sorted_sections_gscene_label()

    def sorted_sections_image_updated(self):
        filename = self.sorted_sections_gscene.active_section
        self.label_sorted_sections_filename.setText(filename)
        self.label_sorted_sections_index.setText(str(self.sorted_sections_gscene.active_section))
        if filename == 'Placeholder' or filename == 'Rescan':
            return
        assert filename != 'Unknown' and filename != 'Nonexisting'

        # Update slide scene

        # slide_name = self.filename_to_slide[filename]
        # position = self.slide_position_to_fn[slide_name].keys()[self.slide_position_to_fn[slide_name].values().index(filename)]
        # self.slide_gscene.set_active_section(slide_name)
        self.update_sorted_sections_gscene_label()

    def update_sorted_sections_gscene_label(self):
        """
        Set the label next to sortedSectionGscene to FIRST, LAST or ANCHOR.
        """

        # print self.sorted_sections_gscene.active_section, self.anchor_fn
        # if self.sorted_sections_gscene.active_section is not None:
        #     print self.sorted_filenames[self.sorted_sections_gscene.active_section-1]

        if self.sorted_sections_gscene.active_section == self.first_section:
            self.label_sorted_sections_status.setText('FIRST')
        elif self.sorted_sections_gscene.active_section == self.last_section:
            self.label_sorted_sections_status.setText('LAST')
        elif hasattr(self, 'anchor_fn') and self.sorted_sections_gscene.active_section is not None and \
            self.sorted_sections_gscene.active_section == self.anchor_fn:
            print(self.sorted_sections_gscene.active_section)
            self.label_sorted_sections_status.setText('ANCHOR')
        else:
            self.label_sorted_sections_status.setText('')

    def set_status(self, slide_name, position, fn):
        """
        Update slide_position_to_fn variables.
        If active, change content and captions of the specified slide position gscene.
        """

        # old_fn = self.slide_position_to_fn[slide_name][position]
        self.slide_position_to_fn[slide_name][position] = fn

        # # if slide_name == 'N_92':
        # print position
        # print self.slide_position_gscenes[position].data_feeder.all_sections
        # print self.section_image_feeders[slide_name].all_sections

        if slide_name == self.slide_gscene.active_section:
            self.slide_position_gscenes[position].set_active_section(fn)
            self.labels_slide_position_filename[position].setText(fn)

            if hasattr(self, 'sorted_filenames'):
                if fn == 'Placeholder' or fn == 'Rescan' or fn == 'Nonexisting':
                    self.labels_slide_position_index[position].setText('')
                else:
                    if fn in self.sorted_filenames:
                        self.labels_slide_position_index[position].setText(str(self.sorted_filenames.index(fn)+1))
                    else:
                        self.labels_slide_position_index[position].setText('Not in sorted list.')


    # def slide_image_updated(self):
    #     self.setWindowTitle('Slide %(slide_index)s' % {'slide_index': self.slide_gscene.active_section})
    #
    #     slide_name = self.slide_gscene.active_section
    #     feeder = self.section_image_feeders[slide_name]
    #
    #     if slide_name not in self.slide_position_to_fn:
    #         self.slide_position_to_fn[slide_name] = {p: 'Unknown' for p in [1,2,3]}
    #
    #     for position, gscene in self.slide_position_gscenes.iteritems():
    #
    #         gscene.set_data_feeder(feeder)
    #
    #         if self.slide_position_to_fn[slide_name][position] != 'Unknown':
    #             self.set_status(slide_name, position, self.slide_position_to_fn[slide_name][position])
    #         else:
    #             if position in self.thumbnail_filenames[slide_name]:
    #                 newest_fn = sorted(self.thumbnail_filenames[slide_name][position].items())[-1][1]
    #                 self.set_status(slide_name, position, newest_fn)
    #             else:
    #                 self.set_status(slide_name, position, 'Nonexisting')

    def show_option_changed(self, index):

        show_option_text = str(self.sender().currentText())
        if show_option_text == 'Original Aligned':
            self.set_show_option('aligned')
        elif show_option_text == 'Original':
            self.set_show_option('original')
        elif show_option_text == 'Mask Contoured':
            self.set_show_option('mask_contour')
        else:
            raise Exception('Not implemented.')

    def set_show_option(self, showing):

        if self.currently_showing == showing:
            return
        else:
            self.currently_showing = showing
            print(self.currently_showing)
            self.update_sorted_sections_gscene_from_sorted_filenames()

    def slide_position_image_updated(self):
        position = self.sender().id
        self.set_status(self.slide_gscene.active_section, position, self.slide_position_gscenes[position].active_section)

    def eventFilter(self, obj, event):

        if event.type() == QEvent.GraphicsSceneMousePress:
            pass
        elif event.type() == QEvent.KeyPress:
            key = event.key()
        return False
Exemplo n.º 6
0
class GUICropBrainStem(QWidget):
    def __init__(self, stack, parent=None):
        super(GUICropBrainStem, self).__init__(parent)

        self.stack = stack
        self.fileLocationManager = FileLocationManager(self.stack)
        self.sqlController = SqlController()
        self.sqlController.get_animal_info(self.stack)

        self.valid_sections = self.sqlController.get_valid_sections(stack)
        self.valid_section_keys = sorted(list(self.valid_sections))

        self.curr_section_index = 0
        self.curr_section = None

        self.active_selection = ''
        self.img_width = 2001
        self.img_height = 1001

        self.rostral = -1
        self.caudal = -1
        self.ventral = -1
        self.dorsal = -1
        self.first_slice = -1
        self.last_slice = -1

        self.init_ui()

        self.b_rostral.clicked.connect(
            lambda: self.click_button(self.b_rostral))
        self.b_caudal.clicked.connect(lambda: self.click_button(self.b_caudal))
        self.b_dorsal.clicked.connect(lambda: self.click_button(self.b_dorsal))
        self.b_ventral.clicked.connect(
            lambda: self.click_button(self.b_ventral))
        self.b_first_slice.clicked.connect(
            lambda: self.click_button(self.b_first_slice))
        self.b_last_slice.clicked.connect(
            lambda: self.click_button(self.b_last_slice))
        self.b_done.clicked.connect(lambda: self.click_button(self.b_done))
        self.viewer.click.connect(self.click_photo)

    def init_ui(self):
        self.font_h1 = QFont("Arial", 32)
        self.font_p1 = QFont("Arial", 16)

        self.grid_top = QGridLayout()
        self.grid_body_upper = QGridLayout()
        self.grid_body = QGridLayout()
        self.grid_body_lower = QGridLayout()

        self.resize(1600, 1100)

        # Grid Top
        self.e_title = QLineEdit()
        self.e_title.setAlignment(Qt.AlignCenter)
        self.e_title.setFont(self.font_h1)
        self.e_title.setReadOnly(True)
        self.e_title.setText("Setup Sorted Filenames")
        self.e_title.setFrame(False)
        self.grid_top.addWidget(self.e_title, 0, 0)

        self.b_help = QPushButton("HELP")
        self.b_help.setDefault(True)
        self.b_help.setEnabled(True)
        self.grid_top.addWidget(self.b_help, 0, 1)

        # Grid BODY UPPER
        self.e_filename = QLineEdit()
        self.e_filename.setAlignment(Qt.AlignCenter)
        self.e_filename.setFont(self.font_p1)
        self.e_filename.setReadOnly(True)
        self.e_filename.setText("Filename: ")
        self.grid_body_upper.addWidget(self.e_filename, 0, 2)

        self.e_section = QLineEdit()
        self.e_section.setAlignment(Qt.AlignCenter)
        self.e_section.setFont(self.font_p1)
        self.e_section.setReadOnly(True)
        self.e_section.setText("Section: ")
        self.grid_body_upper.addWidget(self.e_section, 0, 3)

        # Grid BODY
        self.viewer = ImageViewer(self)
        self.grid_body.addWidget(self.viewer, 0, 0)

        # Grid BODY LOWER
        self.b_rostral = QPushButton("Rostral Limit:")
        self.grid_body_lower.addWidget(self.b_rostral, 0, 0)

        self.e_rostral = QLineEdit()
        self.e_rostral.setAlignment(Qt.AlignCenter)
        self.e_rostral.setFont(self.font_p1)
        self.grid_body_lower.addWidget(self.e_rostral, 0, 1)

        self.b_caudal = QPushButton("Caudal Limit:")
        self.grid_body_lower.addWidget(self.b_caudal, 1, 0)

        self.e_caudal = QLineEdit()
        self.e_caudal.setAlignment(Qt.AlignCenter)
        self.e_caudal.setFont(self.font_p1)
        self.grid_body_lower.addWidget(self.e_caudal, 1, 1)

        self.b_dorsal = QPushButton("Dorsal Limit:")
        self.grid_body_lower.addWidget(self.b_dorsal, 0, 2)

        self.e_dorsal = QLineEdit()
        self.e_dorsal.setAlignment(Qt.AlignCenter)
        self.e_dorsal.setFont(self.font_p1)
        self.grid_body_lower.addWidget(self.e_dorsal, 0, 3)

        self.b_ventral = QPushButton("Ventral Limit:")
        self.grid_body_lower.addWidget(self.b_ventral, 1, 2)

        self.e_ventral = QLineEdit()
        self.e_ventral.setAlignment(Qt.AlignCenter)
        self.e_ventral.setFont(self.font_p1)
        self.grid_body_lower.addWidget(self.e_ventral, 1, 3)

        self.b_first_slice = QPushButton("Mark as FIRST Slice With Brainstem:")
        self.grid_body_lower.addWidget(self.b_first_slice, 0, 4)

        self.e_first_slice = QLineEdit()
        self.e_first_slice.setAlignment(Qt.AlignCenter)
        self.e_first_slice.setFont(self.font_p1)
        self.grid_body_lower.addWidget(self.e_first_slice, 0, 5)

        self.b_last_slice = QPushButton("Mark as LAST Slice With Brainstem:")
        self.grid_body_lower.addWidget(self.b_last_slice, 1, 4)

        self.e_last_slice = QLineEdit()
        self.e_last_slice.setAlignment(Qt.AlignCenter)
        self.e_last_slice.setFont(self.font_p1)
        self.grid_body_lower.addWidget(self.e_last_slice, 1, 5)

        self.b_done = QPushButton("DONE")
        self.grid_body_lower.addWidget(self.b_done, 1, 6)

        # Super grid
        self.supergrid = QGridLayout()
        self.supergrid.addLayout(self.grid_top, 0, 0)
        self.supergrid.addLayout(self.grid_body_upper, 1, 0)
        self.supergrid.addLayout(self.grid_body, 2, 0)
        self.supergrid.addLayout(self.grid_body_lower, 3, 0)

        # Set layout and window title
        self.setLayout(self.supergrid)
        self.setWindowTitle("Q")

    def set_curr_section(self, section_index=-1):
        """
        Sets the current section to the section passed in.
        Will automatically update curr_section, prev_section, and next_section.
        Updates the header fields and loads the current section image.
        """
        if section_index == -1:
            section_index = self.curr_section_index

        # Update curr, prev, and next section
        self.curr_section_index = section_index
        self.curr_section = self.valid_sections[self.valid_section_keys[
            self.curr_section_index]]

        # Update the section and filename at the top
        self.e_filename.setText(self.curr_section['destination'])
        self.e_section.setText(str(self.curr_section['section_number']))

        # Get filepath of "curr_section" and set it as viewer's photo
        img_fp = os.path.join(self.fileLocationManager.thumbnail_prep,
                              self.curr_section['destination'])
        if os.path.isfile(img_fp):
            img = cv2.imread(img_fp) * 3
            self.img_height, self.img_width, channel = img.shape

            if self.rostral != -1:
                x_coordinate = int(self.rostral)
                img[:, x_coordinate - 2:x_coordinate + 2, :] = np.ones(
                    (self.img_height, 4, 3)) * 255
            if self.caudal != -1:
                x_coordinate = int(self.caudal)
                img[:, x_coordinate - 2:x_coordinate + 2, :] = np.ones(
                    (self.img_height, 4, 3)) * 255
            if self.dorsal != -1:
                y_coordinate = int(self.dorsal)
                img[y_coordinate - 2:y_coordinate + 2, :, :] = np.ones(
                    (4, self.img_width, 3)) * 255
            if self.ventral != -1:
                y_coordinate = int(self.ventral)
                img[y_coordinate - 2:y_coordinate + 2, :, :] = np.ones(
                    (4, self.img_width, 3)) * 255

            qImg = QImage(img.data, self.img_width, self.img_height,
                          3 * self.img_width, QImage.Format_RGB888)
            self.viewer.set_photo(QPixmap(qImg))

        # Update the internal crop values based on text boxes
        #self.updateCropVals()

    def get_valid_section_index(self, section_index):
        if section_index >= len(self.valid_sections):
            return 0
        elif section_index < 0:
            return len(self.valid_sections) - 1
        else:
            return section_index

    def click_button(self, button):
        if button in [
                self.b_rostral, self.b_caudal, self.b_dorsal, self.b_ventral
        ]:
            self.viewer.set_drag_mode(0)

            if button == self.b_rostral:
                self.active_selection = 'rostral'
            elif button == self.b_caudal:
                self.active_selection = 'caudal'
            elif button == self.b_dorsal:
                self.active_selection = 'dorsal'
            elif button == self.b_ventral:
                self.active_selection = 'ventral'

        # Prep2 section limits
        elif button in [self.b_first_slice, self.b_last_slice]:
            if button == self.b_first_slice:
                self.first_slice = int(self.curr_section)
                self.e_first_slice.setText(str(self.curr_section))
            elif button == self.b_last_slice:
                self.last_slice = int(self.curr_section)
                self.e_last_slice.setText(str(self.curr_section))

        elif button == self.b_done:
            if -1 in [
                    self.rostral, self.caudal, self.dorsal, self.ventral,
                    self.first_slice, self.last_slice
            ]:
                QMessageBox.about(self, "Popup Message",
                                  "Make sure all six fields have values!")
                return
            elif self.rostral >= self.caudal:
                QMessageBox.about(
                    self, "Popup Message",
                    "Rostral Limit must be smaller than caudal limit!")
                return
            elif self.dorsal >= self.ventral:
                QMessageBox.about(
                    self, "Popup Message",
                    "Dorsal Limit must be smaller than Ventral limit!")
                return
            elif self.first_slice >= self.last_slice:
                QMessageBox.about(self, "Popup Message",
                                  "Last slice must be after the first slice!")
                return

            try:
                QMessageBox.about(
                    self, "Popup Message",
                    "This operation will take roughly 1.5 minutes per image.")
                self.setCurrSection(self.curr_section)
                stain = stack_metadata[stack]['stain']
                os.subprocess.call([
                    'python', 'a_script_preprocess_6.py', stack, stain, '-l',
                    str(self.rostral),
                    str(self.caudal),
                    str(self.dorsal),
                    str(self.ventral),
                    str(self.first_slice),
                    str(self.last_slice)
                ])
                sys.exit(app.exec_())
            except Exception as e:
                sys.stderr.write('\n ********************************\n')
                sys.stderr.write(str(e))
                sys.stderr.write('\n ********************************\n')

    def click_photo(self, pos):
        if self.viewer.dragMode() == QGraphicsView.NoDrag:
            x = pos.x()
            y = pos.y()
            print('%d, %d' % (pos.x(), pos.y()))

            scale_factor = 1.0 / self.viewer.scale_factor

            if self.active_selection == '':
                pass
            elif self.active_selection == 'rostral':
                self.rostral = int(x * scale_factor)
                self.rostral = min(self.rostral, self.img_width - 5)
                self.rostral = max(self.rostral, 5)
                self.e_rostral.setText(str(self.rostral))
            elif self.active_selection == 'caudal':
                self.caudal = int(x * scale_factor)
                self.caudal = min(self.caudal, self.img_width - 5)
                self.caudal = max(self.caudal, 5)
                self.e_caudal.setText(str(self.caudal))
            elif self.active_selection == 'dorsal':
                self.dorsal = int(y * scale_factor)
                self.dorsal = min(self.dorsal, self.img_height - 5)
                self.dorsal = max(self.dorsal, 5)
                self.e_dorsal.setText(str(self.dorsal))
            elif self.active_selection == 'ventral':
                self.ventral = int(y * scale_factor)
                self.ventral = min(self.ventral, self.img_height - 5)
                self.ventral = max(self.ventral, 5)
                self.e_ventral.setText(str(self.ventral))

            self.active_selection = ''
            self.viewer.set_drag_mode(1)

    def updateCropVals(self):
        if self.e_rostral.text() != '':
            try:
                self.rostral = int(self.e_rostral.text())
            except:
                self.rostral = -1
                self.e_rostral.setText("")
        if self.e_caudal.text() != '':
            try:
                self.caudal = int(self.e_caudal.text())
            except:
                self.caudal = -1
                self.e_caudal.setText("")
        if self.e_dorsal.text() != '':
            try:
                self.dorsal = int(self.e_dorsal.text())
            except:
                self.dorsal = -1
                self.e_dorsal.setText("")
        if self.e_ventral.text() != '':
            try:
                self.ventral = int(self.e_ventral.text())
            except:
                self.ventral = -1
                self.e_ventral.setText("")
        if self.e_first_slice.text() != '':
            try:
                self.first_slice = int(self.e_first_slice.text())
            except:
                self.first_slice = -1
                self.e_first_slice.setText("")
        if self.e_last_slice.text() != '':
            try:
                self.last_slice = int(self.e_last_slice.text())
            except:
                self.last_slice = -1
                self.e_last_slice.setText("")

    def keyPressEvent(self, event):
        try:
            key = event.key()
        except AttributeError:
            key = event

        if key == 91:  # [
            index = self.get_valid_section_index(self.curr_section_index - 1)
            self.set_curr_section(index)
        elif key == 93:  # ]
            index = self.get_valid_section_index(self.curr_section_index + 1)
            self.set_curr_section(index)
        elif key == 16777220:  # Enter
            index = self.get_valid_section_index(self.curr_section_index)
            self.set_curr_section(index)
        else:
            print(key)

    def closeEvent(self, event):
        sys.exit(app.exec_())
Exemplo n.º 7
0
class init_GUI(QWidget):
    def __init__(self, stack, parent=None):
        super(init_GUI, self).__init__(parent)
        self.font_h1 = QFont("Arial", 32)
        self.font_p1 = QFont("Arial", 16)

        self.stack = stack
        self.fileLocationManager = FileLocationManager(self.stack)
        self.sqlController = SqlController()
        self.sqlController.get_animal_info(self.stack)
        self.valid_sections = self.sqlController.get_valid_sections(stack)
        self.valid_section_keys = sorted(list(self.valid_sections))
        section_length = len(self.valid_section_keys)
        self.curr_section_index = section_length // 2
        self.prev_section_index = self.curr_section_index
        self.next_section_index = self.curr_section_index
        self.curr_section = self.valid_sections[self.valid_section_keys[
            self.curr_section_index]]['destination']
        self.prev_section = self.getPrevValidSection(self.curr_section_index)
        self.next_section = self.getNextValidSection(self.curr_section_index)

        self.curr_T = None

        self.mode = 'view'
        # self.mode = 'align'

        self.transform_type = 'pairwise'  # Can toggle to 'anchor'

        # Increasing this number will brighten the images
        self.curr_img_multiplier = 1
        self.prev_img_multiplier = 1

        self.initUI()

    def initUI(self):
        # Set Layout and Geometry of Window
        self.grid_top = QGridLayout()
        self.grid_body_upper = QGridLayout()
        self.grid_body = QGridLayout()
        self.grid_body_lower_align_mode = QGridLayout()
        self.grid_bottom = QGridLayout()
        self.grid_blank = QGridLayout()

        # self.setFixedSize(1600, 1100)
        self.resize(1600, 1100)

        ### VIEWER ### (Grid Body)
        self.viewer = ImageViewer(self)
        self.viewer.photoClicked.connect(self.photoClicked)

        ### Grid TOP ###
        # Static Text Field (Title)
        self.e1 = QLineEdit()
        self.e1.setValidator(QIntValidator())
        self.e1.setAlignment(Qt.AlignCenter)
        self.e1.setFont(self.font_h1)
        self.e1.setReadOnly(True)
        self.e1.setText("Quality Checker")
        self.e1.setFrame(False)
        self.grid_top.addWidget(self.e1, 0, 0)
        # Button Text Field
        self.b_help = QPushButton("HELP")
        self.b_help.setDefault(True)
        self.b_help.setEnabled(True)
        self.b_help.clicked.connect(
            lambda: self.help_button_press(self.b_help))
        self.b_help.setStyleSheet(
            "color: rgb(0,0,0); background-color: rgb(250,250,250);")
        self.grid_top.addWidget(self.b_help, 0, 1)

        ### Grid BODY UPPER ###
        # Static Text Field
        self.e2 = QLineEdit()
        self.e2.setAlignment(Qt.AlignCenter)
        self.e2.setFont(self.font_p1)
        self.e2.setReadOnly(True)
        self.e2.setText("Filename: ")
        self.e2.setStyleSheet(
            "color: rgb(50,50,250); background-color: rgb(250,250,250);")
        self.grid_body_upper.addWidget(self.e2, 0, 0)
        # Static Text Field
        self.e3 = QLineEdit()
        self.e3.setAlignment(Qt.AlignCenter)
        self.e3.setFont(self.font_p1)
        self.e3.setReadOnly(True)
        self.e3.setText("Section: ")
        self.e3.setStyleSheet(
            "color: rgb(50,50,250); background-color: rgb(250,250,250);")
        self.grid_body_upper.addWidget(self.e3, 0, 1)
        # Static Text Field
        self.e4 = QLineEdit()
        self.e4.setAlignment(Qt.AlignCenter)
        self.e4.setFont(self.font_p1)
        self.e4.setReadOnly(True)
        self.e4.setText("Filename: ")
        self.e4.setStyleSheet(
            "color: rgb(250,50,50); background-color: rgb(250,250,250);")
        self.grid_body_upper.addWidget(self.e4, 0, 2)
        # Static Text Field
        self.e5 = QLineEdit()
        self.e5.setAlignment(Qt.AlignCenter)
        self.e5.setFont(self.font_p1)
        self.e5.setReadOnly(True)
        self.e5.setText("Section: ")
        self.e5.setStyleSheet(
            "color: rgb(250,50,50); background-color: rgb(250,250,250);")
        self.grid_body_upper.addWidget(self.e5, 0, 3)

        ### Grid BODY ###
        # Custom VIEWER
        self.grid_body.addWidget(self.viewer, 0, 0)

        ### Grid BODY LOWER (align mode only) ###
        # Button Text Field
        self.b1 = QPushButton("Brighten blue image")
        self.b1.setDefault(True)
        self.b1.setEnabled(False)
        self.b1.clicked.connect(lambda: self.buttonPress(self.b1))
        self.b1.setStyleSheet(
            "color: rgb(50,50,250); background-color: rgb(250,250,250);")
        self.grid_body_lower_align_mode.addWidget(self.b1, 0, 0)
        # Button Text Field
        self.b2 = QPushButton("Brighten red image")
        self.b2.setDefault(True)
        self.b2.setEnabled(False)
        self.b2.clicked.connect(lambda: self.buttonPress(self.b2))
        self.b2.setStyleSheet(
            "color: rgb(250,50,50); background-color: rgb(250,250,250);")
        self.grid_body_lower_align_mode.addWidget(self.b2, 1, 0)
        # Button Text Field
        self.b_up = QPushButton("/\\")
        self.b_up.setDefault(True)
        self.b_up.setEnabled(False)
        self.b_up.clicked.connect(lambda: self.buttonPress(self.b_up))
        self.b_up.setStyleSheet(
            "color: rgb(250,50,50); background-color: rgb(250,250,250);")
        self.grid_body_lower_align_mode.addWidget(self.b_up, 0, 3)
        # Button Text Field
        self.b_left = QPushButton("<=")
        self.b_left.setDefault(True)
        self.b_left.setEnabled(False)
        self.b_left.clicked.connect(lambda: self.buttonPress(self.b_left))
        self.b_left.setStyleSheet(
            "color: rgb(250,50,50); background-color: rgb(250,250,250);")
        self.grid_body_lower_align_mode.addWidget(self.b_left, 1, 2)
        # Button Text Field
        self.b_down = QPushButton("\/")
        self.b_down.setDefault(True)
        self.b_down.setEnabled(False)
        self.b_down.clicked.connect(lambda: self.buttonPress(self.b_down))
        self.b_down.setStyleSheet(
            "color: rgb(250,50,50); background-color: rgb(250,250,250);")
        self.grid_body_lower_align_mode.addWidget(self.b_down, 1, 3)
        # Button Text Field
        self.b_right = QPushButton("=>")
        self.b_right.setDefault(True)
        self.b_right.setEnabled(False)
        self.b_right.clicked.connect(lambda: self.buttonPress(self.b_right))
        self.b_right.setStyleSheet(
            "color: rgb(250,50,50); background-color: rgb(250,250,250);")
        self.grid_body_lower_align_mode.addWidget(self.b_right, 1, 4)
        # Button Text Field
        self.b_clockwise = QPushButton("^--'")
        self.b_clockwise.setDefault(True)
        self.b_clockwise.setEnabled(False)
        self.b_clockwise.clicked.connect(
            lambda: self.buttonPress(self.b_clockwise))
        self.b_clockwise.setStyleSheet(
            "color: rgb(250,50,50); background-color: rgb(250,250,250);")
        self.grid_body_lower_align_mode.addWidget(self.b_clockwise, 0, 6)
        # Button Text Field
        self.b_cclockwise = QPushButton("'--^")
        self.b_cclockwise.setDefault(True)
        self.b_cclockwise.setEnabled(False)
        self.b_cclockwise.clicked.connect(
            lambda: self.buttonPress(self.b_cclockwise))
        self.b_cclockwise.setStyleSheet(
            "color: rgb(250,50,50); background-color: rgb(250,250,250);")
        self.grid_body_lower_align_mode.addWidget(self.b_cclockwise, 0, 7)
        # Button Text Field
        self.b_save_transform = QPushButton("Save Transformation")
        self.b_save_transform.setDefault(True)
        self.b_save_transform.setEnabled(False)
        self.b_save_transform.clicked.connect(
            lambda: self.buttonPress(self.b_save_transform))
        self.b_save_transform.setStyleSheet(
            "color: rgb(0,0,0); background-color: rgb(250,200,250);")
        self.grid_body_lower_align_mode.addWidget(self.b_save_transform, 1, 9)
        # Button Text Field
        self.b_done = QPushButton("DONE")
        self.b_done.setDefault(True)
        self.b_done.setEnabled(True)
        self.b_done.clicked.connect(lambda: self.buttonPress(self.b_done))
        self.b_done.setStyleSheet(
            "color: rgb(0,0,0); background-color: rgb(200,250,250);")
        self.grid_body_lower_align_mode.addWidget(self.b_done, 1, 10)

        # Grid stretching
        self.grid_body_upper.setColumnStretch(0, 2)
        self.grid_body_upper.setColumnStretch(2, 2)

        ### SUPERGRID ###
        self.supergrid = QGridLayout()
        self.supergrid.addLayout(self.grid_top, 0, 0)
        self.supergrid.addLayout(self.grid_body_upper, 1, 0)
        self.supergrid.addLayout(self.grid_body, 2, 0)
        self.supergrid.addLayout(self.grid_body_lower_align_mode, 3, 0)
        self.supergrid.addLayout(self.grid_bottom, 4, 0)

        # Set layout and window title
        self.setLayout(self.supergrid)
        self.setWindowTitle("Q")

        # Loads self.curr_section as the current image and sets all fields appropriatly
        self.setCurrSection(self.curr_section_index)

    def help_button_press(self, button):
        info_text = "This GUI is used to align slices to each other. The shortcut commands are as follows: \n\n\
    -  `m`: Toggle between view mode (grayscale) and alignment mode (red & blue).\n\
    -  `[`: Go back one section. \n\
    -  `]`: Go forward one section. \n\n\
    \
    All changes must be done in alignment mode. Alignment mode will display the pairwise alignment between the current \
active section (red) and the previous section (blue). Using the buttons at the foot of the GUI, you can translate and \
rotate the active section (red) as well as brighten either the active or previous section. Adjust the red slice such \
that it is aligned to the blue slice as well as possible and press \"Save Transformation\".\n\n\
    \
    The grayscale images should all be aligned to one another. Please verify that all sections are aligned properly \
before you finish this step."

        QMessageBox.information(self, "Empty Field", info_text)

    def loadImage(self):
        # Get filepath of "curr_section" and set it as viewer's photo
        #fp = get_fp(self.curr_section, prep_id=1)
        input_tif = os.path.join(self.fileLocationManager.thumbnail_prep,
                                 self.curr_section)
        print('input_tif', input_tif)
        #fp = io.imread(input_tif)
        self.viewer.setPhoto(QPixmap(input_tif))
        self.curr_T = None

    #
    # img, T = get_transformed_image( self.curr_section,
    #                                 transformation='anchor',
    #                                 prev_section=self.prev_section )

    # T = get_comulative_pairwise_transform( stack,
    #               DataManager.metadata_cache['sections_to_filenames'][stack][self.curr_section] )
    # img = apply_transform( stack, T,
    #               DataManager.metadata_cache['sections_to_filenames'][stack][self.curr_section])

    # height, width, channel = img.shape
    # bytesPerLine = 3 * width
    # qImg = QImage(img.data, width, height, bytesPerLine, QImage.Format_RGB888)
    #
    # self.viewer.setPhoto( QPixmap( qImg ) )

    def loadImageThenNormalize(self):
        # Get filepath of "curr_section" and set it as viewer's photo
        input_tif = os.path.join(self.fileLocationManager.oriented,
                                 self.curr_section)
        #fp = imread(input_tif)
        img = io.imread(input_tif) * 3

        height, width, channel = img.shape
        bytesPerLine = 3 * width
        qImg = QImage(img.data, width, height, bytesPerLine,
                      QImage.Format_RGB888)

        self.viewer.setPhoto(QPixmap(qImg))

    def loadImagesColored(self):
        # we want to load RED (current section) and BLUE (previous section) channels overlayed
        curr_tif = os.path.join(self.fileLocationManager.oriented,
                                self.curr_section)
        img_curr_red = io.imread(curr_tif)
        prev_tif = os.path.join(self.fileLocationManager.oriented,
                                self.prev_section)
        img_prev_blue = io.imread(prev_tif)
        # Load the untransformed images
        # img_curr_red = cv2.imread( fp_curr_red )
        # img_prev_blue = cv2.imread( fp_prev_blue )

        if self.transform_type == 'anchor':
            # Load anchor-transformed images
            img_curr_red, T = get_transformed_image(self.curr_section,
                                                    transformation='anchor')
            img_prev_blue, Tb = get_transformed_image(self.prev_section,
                                                      transformation='anchor')
            self.curr_T = T
        elif self.transform_type == 'pairwise' and self.curr_section > self.prev_section:
            # Load pairwise-transformed images
            # The current red image is transformed to the previous blue image
            img_curr_red, T = get_transformed_image(
                self.curr_section,
                transformation='pairwise',
                prev_section=self.prev_section)
            self.curr_T = T
            # Blue image does not change
            prev_tif = os.path.join(self.fileLocationManager.oriented,
                                    self.prev_section)
            img_prev_blue = io.imread(prev_tif)
        elif self.transform_type == 'pairwise' and self.curr_section < self.prev_section:
            # In the case of wrapping (prev_section wrapps to the last section when curr_section is 0)
            #   We just load the red ad blue channels of curr_section making it appear purple
            self.curr_T = None

            curr_tif = os.path.join(self.fileLocationManager.oriented,
                                    self.curr_section)
            img_curr_red = io.imread(curr_tif)
            prev_tif = os.path.join(self.fileLocationManager.oriented,
                                    self.prev_section)
            img_prev_blue = io.imread(prev_tif)

        height_r, width_r, _ = img_curr_red.shape
        height_b, width_b, _ = img_prev_blue.shape
        new_height = max(height_r, height_b)
        new_width = max(width_r, width_b)

        img_combined = np.ones((new_height, new_width, 3))
        img_combined[0:height_r, 0:width_r, 0] += img_curr_red[:, :, 0]
        img_combined[0:height_b, 0:width_b, 2] += img_prev_blue[:, :, 0]

        img_combined = np.array(
            img_combined,
            dtype=np.uint8)  # This line only change the type, not values

        # Create a "qImg" which allows you to create a QPixmap from a matrix
        bytesPerLine = 3 * new_width
        qImg = QImage(img_combined.data * 2, new_width, new_height,
                      bytesPerLine, QImage.Format_RGB888)

        pixmap = QPixmap(qImg)
        # pixmap = pixmap.scaled(640,400, Qt.KeepAspectRatio)

        self.viewer.setPhoto(pixmap)

    def transformImagesColored(self):
        # we want to load RED (current section) and BLUE (previous section) channels overlayed
        #fp_curr_red = get_fp(self.curr_section)
        #fp_prev_blue = get_fp(self.prev_section)

        curr_tif = os.path.join(self.fileLocationManager.oriented,
                                self.curr_section)
        fp_curr_red = io.imread(curr_tif)
        prev_tif = os.path.join(self.fileLocationManager.oriented,
                                self.prev_section)
        fp_prev_blue = io.imread(prev_tif)

        if self.transform_type == 'anchor':
            # Load anchor-transformed images
            img_curr_red = apply_transform(stack, self.curr_T,
                                           self.curr_section)
            img_prev_blue, Tb = get_transformed_image(self.prev_section,
                                                      transformation='anchor')
        elif self.transform_type == 'pairwise':
            # Load pairwise-transformed images
            # The current red image is transformed to the previous blue image
            img_curr_red = apply_transform(stack, self.curr_T,
                                           self.curr_section)
            # Blue image does not change
            img_prev_blue = io.imread(fp_prev_blue)

        height_r, width_r, _ = img_curr_red.shape
        height_b, width_b, _ = img_prev_blue.shape
        new_height = max(height_r, height_b)
        new_width = max(width_r, width_b)

        img_combined = np.ones((new_height, new_width, 3))
        img_combined[0:height_r, 0:width_r,
                     0] += img_curr_red[:, :, 0] * self.curr_img_multiplier
        img_combined[0:height_b, 0:width_b,
                     2] += img_prev_blue[:, :, 0] * self.prev_img_multiplier

        img_combined = np.array(
            img_combined,
            dtype=np.uint8)  # This line only change the type, not values

        # Create a "qImg" which allows you to create a QPixmap from a matrix
        bytesPerLine = 3 * new_width
        qImg = QImage(img_combined.data * 2, new_width, new_height,
                      bytesPerLine, QImage.Format_RGB888)

        pixmap = QPixmap(qImg)
        # pixmap = pixmap.scaled(640,400, Qt.KeepAspectRatio)

        self.viewer.setPhoto(pixmap)

    def photoClicked(self, pos):
        if self.viewer.dragMode() == QGraphicsView.NoDrag:
            print('%d, %d' % (pos.x(), pos.y()))

    def pixInfo(self):
        self.viewer.toggleDragMode()

    def keyPressEvent(self, event):
        try:
            key = event.key()
        except AttributeError:
            key = event

        if key == 91:  # [
            self.getPrevValidSection(self.curr_section_index)
            self.setCurrSection(self.prev_section_index)
        elif key == 93:  # ]
            self.getNextValidSection(self.curr_section_index)
            self.setCurrSection(self.next_section_index)
        elif key == 81:  # Q
            self.pixInfo()
        elif key == 77:  # M
            self.toggleMode()
        else:
            print(key)

    def setCurrSection(self, section_index=-1):
        """
        Sets the current section to the section passed in.
        Will automatically update curr_section, prev_section, and next_section.
        Updates the header fields and loads the current section image.
        """
        if section_index == -1:
            section_index = self.curr_section_index

        # Update curr, prev, and next section
        self.curr_section_index = section_index
        self.curr_section = self.valid_sections[self.valid_section_keys[
            self.curr_section_index]]['destination']
        self.prev_section = self.getPrevValidSection(self.curr_section_index)
        self.next_section = self.getNextValidSection(self.curr_section_index)

        self.updateCurrHeaderFields()
        self.updatePrevHeaderFields()

        self.curr_img_multiplier = 1
        self.prev_img_multiplier = 1

        if self.mode == 'view':
            self.loadImage()
            self.toggle_align_buttons(enabled=False)

        elif self.mode == 'align':
            self.loadImagesColored()

            self.toggle_align_buttons(enabled=True)

    def toggleMode(self):
        if self.mode == 'view':
            self.mode = 'align'
        elif self.mode == 'align':
            self.mode = 'view'
        self.setCurrSection()

    def buttonPress(self, button):
        # Brighten an image
        if button in [self.b1, self.b2]:
            if button == self.b1:
                self.prev_img_multiplier += 1
                if self.prev_img_multiplier > 5:
                    self.prev_img_multiplier = 1
            if button == self.b2:
                self.curr_img_multiplier += 1
                if self.curr_img_multiplier > 5:
                    self.curr_img_multiplier = 1

        # Translate the red image and update T matrix
        if button in [self.b_right, self.b_left, self.b_up, self.b_down]:
            if button == self.b_right:
                self.curr_T[0, 2] += 3
            if button == self.b_left:
                self.curr_T[0, 2] -= 3
            if button == self.b_up:
                self.curr_T[1, 2] -= 3
            if button == self.b_down:
                self.curr_T[1, 2] += 3
            # self.transformImagesColored()

        # Rotate the red image and update T matrix
        if button in [self.b_clockwise, self.b_cclockwise]:
            if button == self.b_clockwise:
                degrees = 0.3
            if button == self.b_cclockwise:
                degrees = -0.3
            # Update matrix's rotation fields
            self.curr_T[0, 0] = math.cos(
                math.acos(self.curr_T[0, 0]) + degrees * (math.pi / 180))
            self.curr_T[1, 1] = math.cos(
                math.acos(self.curr_T[1, 1]) + degrees * (math.pi / 180))
            self.curr_T[0, 1] = -math.sin(
                math.asin(-self.curr_T[0, 1]) + degrees * (math.pi / 180))
            self.curr_T[1, 0] = math.sin(
                math.asin(self.curr_T[1, 0]) + degrees * (math.pi / 180))

        if button == self.b_save_transform:
            self.saveCurrTransform()
            self.update_prep1_images()

        if button == self.b_done:
            sys.exit(app.exec_())

        self.transformImagesColored()

    def getNextValidSection(self, section_index):
        self.next_section_index = section_index + 1
        if self.next_section_index > len(self.valid_sections) - 1:
            self.next_section_index = 0
        self.next_section = self.valid_sections[self.valid_section_keys[
            self.next_section_index]]['destination']
        return self.next_section

    def getPrevValidSection(self, section_index):
        self.prev_section_index = int(section_index) - 1
        if self.prev_section_index < 0:
            self.prev_section_index = len(self.valid_sections) - 1
        self.prev_section = self.valid_sections[self.valid_section_keys[
            self.prev_section_index]]['destination']
        return self.prev_section

    def toggle_align_buttons(self, enabled):
        self.b1.setEnabled(enabled)
        self.b2.setEnabled(enabled)
        self.b_right.setEnabled(enabled)
        self.b_left.setEnabled(enabled)
        self.b_up.setEnabled(enabled)
        self.b_down.setEnabled(enabled)
        self.b_clockwise.setEnabled(enabled)
        self.b_cclockwise.setEnabled(enabled)
        self.b_save_transform.setEnabled(enabled)

    def updateCurrHeaderFields(self):
        label = self.valid_sections[self.valid_section_keys[
            self.curr_section_index]]['source']
        self.e4.setText(label)
        self.e5.setText(self.curr_section)

    def updatePrevHeaderFields(self):
        label = self.valid_sections[self.valid_section_keys[
            self.prev_section_index]]['source']
        self.e2.setText(label)
        self.e3.setText(self.prev_section)

    def update_prep1_images(self):
        version = stain_to_metainfo[self.stain]['img_version_1']
        create_input_spec_ini_all(name='input_spec.ini', \
                                  stack=stack, prep_id='None', version=version, resol='thumbnail')

        # Call "compose" to regenerate the csv file
        command = [
            'python', 'compose_v3.py', 'input_spec.ini', '--op',
            'from_none_to_aligned'
        ]
        completion_message = 'Finished creating transforms to anchor csv file.'
        call_and_time(command, completion_message=completion_message)

        # Apply transformations from csv and save as prep1 images
        command = [
            'python', 'warp_crop_v3.py', '--input_spec', 'input_spec.ini',
            '--op_id', 'from_none_to_padded', '--njobs', '8', '--pad_color',
            get_padding_color(stack)
        ]
        completion_message = 'Finished transformation to padded (prep1).'
        call_and_time(command, completion_message=completion_message)

    def saveCurrTransform(self):
        if self.transform_type == 'pairwise':
            curr_section_fn = self.sections_to_filenames[self.curr_section]
            prev_section_fn = self.sections_to_filenames[self.prev_section]

            custom_transform_txt_name = '{}_to_{}.txt'.format(
                curr_section_fn, prev_section_fn)
            custom_transform_txt_file = os.path.join(
                self.fileLocationManager.custom_transform,
                custom_transform_txt_name)

            custom_transform_img_name = '{}_{}.txt'.format(
                curr_section_fn, prev_section_fn)
            custom_transform_img_file = os.path.join(
                self.fileLocationManager.aligned_to, custom_transform_img_name)
            T = self.curr_T

            # Saves the transformed image since we gave a specific output_fp
            apply_transform(stack, self.curr_T, self.curr_section,
                            custom_transform_img_file)

            with open(custom_transform_txt_file, 'w') as file:
                file.write(str(T[0, 0]) + ' ' + str(T[0, 1]) + ' ' + str(T[0, 2]) + ' ' + \
                           str(T[1, 0]) + ' ' + str(T[1, 1]) + ' ' + str(T[1, 2]))
        else:
            pass
Exemplo n.º 8
0
class init_GUI(QWidget):
    def __init__(self, stack, parent=None):
        super(init_GUI, self).__init__(parent)
        self.font_h1 = QFont("Arial", 32)
        self.font_p1 = QFont("Arial", 16)
        self.queued_transformations = []

        # create a dataManager object
        self.sqlController = SqlController()
        self.stack = stack
        self.fileLocationManager = FileLocationManager(self.stack)
        self.sqlController.get_animal_info(self.stack)

        self.valid_sections = self.sqlController.get_valid_sections(stack)
        self.valid_section_keys = sorted(list(self.valid_sections))

        section_length = len(self.valid_section_keys)

        self.curr_section_index = section_length // 2
        self.prev_section_index = self.curr_section_index
        self.next_section_index = self.curr_section_index
        self.curr_section = self.valid_sections[self.valid_section_keys[
            self.curr_section_index]]['destination']
        self.prev_section = self.getPrevValidSection(self.curr_section_index)
        self.next_section = self.getNextValidSection(self.curr_section_index)

        self.initUI()

    def initUI(self):
        # Set Layout and Geometry of Window
        self.grid_top = QGridLayout()
        self.grid_body_upper = QGridLayout()
        self.grid_body = QGridLayout()
        self.grid_body_lower = QGridLayout()
        self.grid_bottom = QGridLayout()
        self.grid_blank = QGridLayout()

        # self.setFixedSize(1600, 1000)
        self.resize(1600, 1000)

        ### VIEWER ### (Grid Body)
        self.viewer = ImageViewer(self)
        self.viewer.photoClicked.connect(self.photoClicked)

        ### Grid TOP ###
        # Static Text Field (Title)
        self.e1 = QLineEdit()
        self.e1.setValidator(QIntValidator())
        self.e1.setAlignment(Qt.AlignCenter)
        self.e1.setFont(self.font_h1)
        self.e1.setReadOnly(True)
        self.e1.setText("Orient Images")
        self.e1.setFrame(False)
        self.grid_top.addWidget(self.e1, 0, 0)
        # Button Text Field
        self.b_help = QPushButton("HELP")
        self.b_help.setDefault(True)
        self.b_help.setEnabled(True)
        self.b_help.clicked.connect(
            lambda: self.help_button_press(self.b_help))
        self.b_help.setStyleSheet(
            "color: rgb(0,0,0); background-color: rgb(250,250,250);")
        self.grid_top.addWidget(self.b_help, 0, 1)

        ### Grid BODY UPPER ###
        # Static Text Field
        self.e4 = QLineEdit()
        self.e4.setAlignment(Qt.AlignCenter)
        self.e4.setFont(self.font_p1)
        self.e4.setReadOnly(True)
        self.e4.setText("Filename: ")
        # self.e4.setStyleSheet("color: rgb(250,50,50); background-color: rgb(250,250,250);")
        self.grid_body_upper.addWidget(self.e4, 0, 2)
        # Static Text Field
        self.e5 = QLineEdit()
        self.e5.setAlignment(Qt.AlignCenter)
        self.e5.setFont(self.font_p1)
        self.e5.setReadOnly(True)
        self.e5.setText("Section: ")
        # self.e5.setStyleSheet("color: rgb(250,50,50); background-color: rgb(250,250,250);")
        self.grid_body_upper.addWidget(self.e5, 0, 3)

        ### Grid BODY ###
        # Custom VIEWER
        self.grid_body.addWidget(self.viewer, 0, 0)

        ### Grid BODY LOWER ###
        # Button Text Field
        self.b1 = QPushButton("Flip image(s) across central vertical line")
        self.b1.setDefault(True)
        self.b1.setEnabled(True)
        self.b1.clicked.connect(lambda: self.buttonPress(self.b1))
        self.b1.setStyleSheet(
            "color: rgb(0,0,0); background-color: rgb(250,250,200);")
        self.grid_body_lower.addWidget(self.b1, 0, 0)
        # Button Text Field
        self.b2 = QPushButton("Flop image(s) across central horozontal line")
        self.b2.setDefault(True)
        self.b2.setEnabled(True)
        self.b2.clicked.connect(lambda: self.buttonPress(self.b2))
        self.b2.setStyleSheet(
            "color: rgb(0,0,0); background-color: rgb(250,250,200);")
        self.grid_body_lower.addWidget(self.b2, 1, 0)
        # Button Text Field
        self.b3 = QPushButton("Rotate Image(s)")
        self.b3.setDefault(True)
        self.b3.setEnabled(True)
        self.b3.clicked.connect(lambda: self.buttonPress(self.b3))
        self.b3.setStyleSheet(
            "color: rgb(0,0,0); background-color: rgb(250,200,250);")
        self.grid_body_lower.addWidget(self.b3, 0, 1, 1, 2)
        # Checkbox
        self.cb_1 = QCheckBox("Apply transformation to ALL images")
        self.cb_1.setChecked(True)
        self.cb_1.setEnabled(False)
        self.grid_body_lower.addWidget(self.cb_1, 0, 3)
        # Static Text Field
        self.e6 = QLineEdit()
        self.e6.setMaximumWidth(250)
        self.e6.setAlignment(Qt.AlignRight)
        self.e6.setReadOnly(True)
        self.e6.setText("Degrees to rotate (clockwise!): ")
        self.grid_body_lower.addWidget(self.e6, 1, 1)
        # Dropbown Menu (ComboBox) for selecting Stack
        self.cb = QComboBox()
        self.cb.addItems(['90', '180', '270'])
        # self.cb.addItems( ['Rotate by 90 degrees', 'Rotate by 180 degrees', 'Rotate by 270 degrees'] )
        # self.cb.addItems( ['45', '90', '135', '180', '225', '270', '315'] )
        self.grid_body_lower.addWidget(self.cb, 1, 2)
        # Button Text Field
        self.b_done = QPushButton("Done orienting")
        self.b_done.setDefault(True)
        self.b_done.setEnabled(True)
        self.b_done.clicked.connect(lambda: self.buttonPress(self.b_done))
        self.b_done.setStyleSheet(
            "color: rgb(0,0,0); background-color: #dfbb19;")
        self.grid_body_lower.addWidget(self.b_done, 1, 3)

        # Grid stretching
        # self.grid_body_upper.setColumnStretch(0, 2)
        self.grid_body_upper.setColumnStretch(2, 2)
        # self.grid_body_lower.setColumnStretch(3, 1)

        ### SUPERGRID ###
        self.supergrid = QGridLayout()
        self.supergrid.addLayout(self.grid_top, 0, 0)
        self.supergrid.addLayout(self.grid_body_upper, 1, 0)
        self.supergrid.addLayout(self.grid_body, 2, 0)
        # self.supergrid.addLayout( self.grid_body_lower, 4, 0)
        self.supergrid.addWidget(QHLine(), 6, 0, 1, 2)
        # self.supergrid.addLayout( self.grid_bottom, 6, 0)
        self.supergrid.addLayout(self.grid_body_lower, 7, 0)
        self.supergrid.addWidget(QHLine(), 8, 0, 1, 2)

        # Set layout and window title
        self.setLayout(self.supergrid)
        self.setWindowTitle("Q")

        # Loads self.curr_section as the current image and sets all fields appropriatly
        self.setCurrSection(self.curr_section_index)

    def help_button_press(self, button):
        info_text = "This GUI is used to align slices to each other. The shortcut commands are as follows: \n\n\
    -  `[`: Go back one section. \n\
    -  `]`: Go forward one section."

        QMessageBox.information(self, "Empty Field", info_text)

    def loadImage(self):
        curr_fn = self.valid_sections[self.valid_section_keys[
            self.curr_section_index]]['destination']
        # Get filepath of "curr_section" and set it as viewer's photo
        img_fp = os.path.join(self.fileLocationManager.thumbnail_prep, curr_fn)
        self.viewer.setPhoto(QPixmap(img_fp))

    def photoClicked(self, pos):
        if self.viewer.dragMode() == QGraphicsView.NoDrag:
            print('%d, %d' % (pos.x(), pos.y()))

    def pixInfo(self):
        self.viewer.toggleDragMode()

    def keyPressEvent(self, event):
        try:
            key = event.key()
        except AttributeError:
            key = event

        if key == 91:  # [
            self.getPrevValidSection(self.curr_section_index)
            self.setCurrSection(self.prev_section_index)
        elif key == 93:  # ]
            self.getNextValidSection(self.curr_section_index)
            self.setCurrSection(self.next_section_index)
        else:
            print(key)

    def setCurrSection(self, section_index=-1):
        """
        Sets the current section to the section passed in.
        Will automatically update curr_section, prev_section, and next_section.
        Updates the header fields and loads the current section image.
        """
        if section_index == -1:
            section_index = self.curr_section_index

        # Update curr, prev, and next section
        self.curr_section_index = section_index
        self.curr_section = self.valid_sections[self.valid_section_keys[
            self.curr_section_index]]['destination']
        self.prev_section = self.getPrevValidSection(self.curr_section_index)
        self.next_section = self.getNextValidSection(self.curr_section_index)
        # Update the section and filename at the top
        self.updateCurrHeaderFields()
        # Update the quality selection in the bottom left
        self.loadImage()

    def getNextValidSection(self, section_index):
        self.next_section_index = section_index + 1
        if self.next_section_index > len(self.valid_sections) - 1:
            self.next_section_index = 0
        self.next_section = self.valid_sections[self.valid_section_keys[
            self.next_section_index]]['destination']
        return self.next_section

    def getPrevValidSection(self, section_index):
        self.prev_section_index = int(section_index) - 1
        if self.prev_section_index < 0:
            self.prev_section_index = len(self.valid_sections) - 1
        self.prev_section = self.valid_sections[self.valid_section_keys[
            self.prev_section_index]]['destination']
        return self.prev_section

    def buttonPress(self, button):
        # Brighten an image
        if button in [self.b1, self.b2, self.b3]:
            # "Flip image(s) across central vertical line"
            if button == self.b1:
                self.transform_thumbnails('flip')
            # "Flop image(s) across central horozontal line"
            elif button == self.b2:
                self.transform_thumbnails('flop')
            # "Rotate Image(s)"
            elif button == self.b3:
                self.transform_thumbnails('rotate',
                                          degrees=str(self.cb.currentText()))
            # Update the Viewer info and displayed image
            self.setCurrSection(self.curr_section_index)
        elif button == self.b_done:
            QMessageBox.about(
                self, "Popup Message",
                "All selected operations will now be performed on the\
                full sized raw images. This may take an hour or two, depending on how many operations are queued."
            )
            self.apply_queued_transformations()
            self.finished()

    def updateCurrHeaderFields(self):
        label = self.valid_sections[self.valid_section_keys[
            self.curr_section_index]]['source']
        self.e4.setText(label)
        self.e5.setText(str(self.curr_section))

    def transform_thumbnails(self, transform_type, degrees=0):
        """
        Transform_type must be "rotate", "flip", or "flop".
        These transformations get applied to all the active sections. The actual
        conversions take place on the thumbnails and the raw files.
        The transformed raw files get placed in the preps/oriented dir.
        """
        if transform_type == 'rotate':
            base_cmd = ['convert', '-' + transform_type, str(degrees)]
        else:
            base_cmd = ['convert', '-' + transform_type]

        self.queued_transformations.append(base_cmd)
        # Apply transforms to just the thumbnails
        THUMBNAIL = self.fileLocationManager.prep_thumbnail
        for k, v in self.valid_sections.items():
            thumbnail = os.path.join(THUMBNAIL, v['destination'])
            subprocess.call(base_cmd + [thumbnail, thumbnail])

    def finished(self):
        self.sqlController.set_step_completed_in_progress_ini(
            self.stack, '1-5_setup_orientations')
        # close_main_gui( ex )
        sys.exit(app.exec_())

    def apply_queued_transformations(self):
        print('queued_transformations', self.queued_transformations)
        if self.queued_transformations == []:
            print('No transformations to do. Exit stage left ...')
        else:
            # Apply to "raw" images
            RAW = self.fileLocationManager.tif
            ORIENTED = self.fileLocationManager.oriented
            for base_cmd in tqdm(self.queued_transformations):
                for k, v in self.valid_sections.items():
                    raw = os.path.join(RAW, v['source'])
                    oriented = os.path.join(ORIENTED, v['destination'])
                    subprocess.call(base_cmd + [raw, oriented])
                    #print(base_cmd + [raw, oriented])
        # Clear the queued transformations
        self.queued_transformations = []
Exemplo n.º 9
0
class GUISortedFilenames(QWidget):
    def __init__(self, stack, parent=None):
        super(GUISortedFilenames, self).__init__(parent)

        self.stack = stack
        self.fileLocationManager = FileLocationManager(self.stack)
        self.sqlController = SqlController()
        self.sqlController.get_animal_info(self.stack)

        self.valid_sections = self.sqlController.get_valid_sections(stack)
        self.valid_section_keys = sorted(list(self.valid_sections))

        self.curr_section_index = 0
        self.curr_section = None

        self.init_ui()

        self.b_rotate_left.clicked.connect(
            lambda: self.click_button(self.b_rotate_left))
        self.b_rotate_right.clicked.connect(
            lambda: self.click_button(self.b_rotate_right))
        self.b_flip_vertical.clicked.connect(
            lambda: self.click_button(self.b_flip_vertical))
        self.b_flip_horozontal.clicked.connect(
            lambda: self.click_button(self.b_flip_horozontal))
        self.b_move_left.clicked.connect(
            lambda: self.click_button(self.b_move_left))
        self.b_move_right.clicked.connect(
            lambda: self.click_button(self.b_move_right))
        self.b_quality.currentIndexChanged.connect(
            lambda: self.click_button(self.b_quality))
        self.b_remove.clicked.connect(lambda: self.click_button(self.b_remove))
        self.b_help.clicked.connect(lambda: self.click_button(self.b_help))
        self.b_done.clicked.connect(lambda: self.click_button(self.b_done))

        self.set_curr_section(self.curr_section_index)

    def init_ui(self):
        self.font_h1 = QFont("Arial", 32)
        self.font_p1 = QFont("Arial", 16)

        self.grid_top = QGridLayout()
        self.grid_body_upper = QGridLayout()
        self.grid_body = QGridLayout()
        self.grid_body_lower = QGridLayout()

        self.resize(1600, 1100)

        # Grid Top
        self.e_title = QLineEdit()
        self.e_title.setAlignment(Qt.AlignCenter)
        self.e_title.setFont(self.font_h1)
        self.e_title.setReadOnly(True)
        self.e_title.setText("Setup Sorted Filenames")
        self.e_title.setFrame(False)
        self.grid_top.addWidget(self.e_title, 0, 0)

        self.b_help = QPushButton("HELP")
        self.b_help.setDefault(True)
        self.b_help.setEnabled(True)
        self.grid_top.addWidget(self.b_help, 0, 1)

        # Grid BODY UPPER
        self.e_filename = QLineEdit()
        self.e_filename.setAlignment(Qt.AlignCenter)
        self.e_filename.setFont(self.font_p1)
        self.e_filename.setReadOnly(True)
        self.e_filename.setText("Filename: ")
        self.grid_body_upper.addWidget(self.e_filename, 0, 2)

        self.e_section = QLineEdit()
        self.e_section.setAlignment(Qt.AlignCenter)
        self.e_section.setFont(self.font_p1)
        self.e_section.setReadOnly(True)
        self.e_section.setText("Section: ")
        self.grid_body_upper.addWidget(self.e_section, 0, 3)

        # Grid BODY
        self.viewer = ImageViewer(self)
        self.grid_body.addWidget(self.viewer, 0, 0)

        # Grid BODY LOWER
        self.b_flip_vertical = QPushButton("Flip vertically")
        self.grid_body_lower.addWidget(self.b_flip_vertical, 0, 0)

        self.b_flip_horozontal = QPushButton("Flop horizontally")
        self.grid_body_lower.addWidget(self.b_flip_horozontal, 0, 1)

        self.b_rotate_left = QPushButton("Rotate Left")
        self.grid_body_lower.addWidget(self.b_rotate_left, 0, 2)

        self.b_rotate_right = QPushButton("Rotate Right")
        self.grid_body_lower.addWidget(self.b_rotate_right, 0, 3)

        self.b_move_left = QPushButton("<--   Move Section Left   <--")
        #self.grid_body_lower.addWidget(self.b_move_left, 1, 0)

        self.b_move_right = QPushButton("-->   Move Section Right   -->")
        #self.grid_body_lower.addWidget(self.b_move_right, 1, 1)

        self.b_quality = QComboBox()
        self.b_quality.addItems([
            'Section quality: unusable', 'Section quality: blurry',
            'Section quality: good'
        ])
        #self.grid_body_lower.addWidget(self.b_quality, 1, 2)

        self.b_remove = QPushButton("Remove section")
        #self.grid_body_lower.addWidget(self.b_remove, 1, 3)

        self.progress = QProgressBar(self)
        self.grid_body_lower.addWidget(self.progress, 2, 0, 1, 3)
        self.progress.hide()

        self.b_done = QPushButton("Finished")
        self.grid_body_lower.addWidget(self.b_done, 2, 3)

        # Super grid
        self.supergrid = QGridLayout()
        self.supergrid.addLayout(self.grid_top, 0, 0)
        self.supergrid.addLayout(self.grid_body_upper, 1, 0)
        self.supergrid.addLayout(self.grid_body, 2, 0)
        self.supergrid.addLayout(self.grid_body_lower, 3, 0)

        # Set layout and window title
        self.setLayout(self.supergrid)
        self.setWindowTitle("Q")

    def set_curr_section(self, section_index=-1):
        """
        Sets the current section to the section passed in.
        Will automatically update curr_section, prev_section, and next_section.
        Updates the header fields and loads the current section image.
        """
        if section_index == -1:
            section_index = self.curr_section_index

        # Update curr, prev, and next section
        self.curr_section_index = section_index
        self.curr_section = self.valid_sections[self.valid_section_keys[
            self.curr_section_index]]

        # Update the section and filename at the top
        self.e_filename.setText(self.curr_section['destination'])
        self.e_section.setText(str(self.curr_section['section_number']))

        # Get filepath of "curr_section" and set it as viewer's photo
        img_fp = os.path.join(self.fileLocationManager.thumbnail_prep,
                              self.curr_section['destination'])
        self.viewer.set_photo(img_fp)

        # Update the quality selection in the bottom left
        index = self.b_quality.findText(self.curr_section['quality'],
                                        Qt.MatchFixedString)
        if index >= 0:
            self.b_quality.setCurrentIndex(index)

    def get_valid_section_index(self, section_index):
        if section_index >= len(self.valid_sections):
            return 0
        elif section_index < 0:
            return len(self.valid_sections) - 1
        else:
            return section_index

    def click_button(self, button):
        if button == self.b_quality:
            curr_section = self.valid_sections[self.valid_section_keys[
                self.curr_section_index]]
            curr_section['quality'] = self.b_quality.currentText()
            self.sqlController.save_valid_sections(self.valid_sections)

        elif button in [self.b_move_left, self.b_move_right, self.b_remove]:
            if button == self.b_move_left:
                self.sqlController.move_section(
                    self.stack, self.curr_section['section_number'], -1)
            elif button == self.b_move_right:
                self.sqlController.move_section(
                    self.stack, self.curr_section['section_number'], 1)
            elif button == self.b_remove:
                result = self.message_box(
                    'Are you sure you want to totally remove this section from this brain?\n\n'
                    +
                    'Warning: The image will be marked as irrelevant to the current brain!',
                    True)

                # The answer is Yes
                if result == 2:
                    # Remove the current section from "self.valid_sections
                    self.sqlController.inactivate_section(
                        self.stack, self.curr_section['section_number'])

                    self.valid_sections = self.sqlController.get_valid_sections(
                        self.stack)
                    self.valid_section_keys = sorted(list(self.valid_sections))

                    if self.curr_section_index == 0:
                        self.curr_section_index = len(
                            self.valid_section_keys) - 1
                    else:
                        self.curr_section_index = self.curr_section_index - 1
            else:
                pass

            # Update the Viewer info and displayed image
            self.valid_sections = self.sqlController.get_valid_sections(
                self.stack)
            self.valid_section_keys = sorted(list(self.valid_sections))
            self.set_curr_section(self.curr_section_index)

        elif button in [
                self.b_flip_vertical, self.b_flip_horozontal,
                self.b_rotate_right, self.b_rotate_left
        ]:
            """
            Transform_type must be "rotate", "flip", or "flop".
            These transformations get applied to all the active sections. The actual
            conversions take place on the thumbnails and the raw files.
            The transformed raw files get placed in the preps/oriented dir.
            """

            index = [
                self.b_flip_vertical, self.b_flip_horozontal,
                self.b_rotate_right, self.b_rotate_left
            ].index(button)
            op = ['flip', 'flop', 'right', 'left'][index]

            size = len(self.valid_sections.values()) - 1
            self.progress_bar(True, size)

            for index, section in enumerate(self.valid_sections.values()):
                thumbnail_path = os.path.join(
                    self.fileLocationManager.thumbnail_prep,
                    section['destination'])
                if os.path.isfile(thumbnail_path):
                    self.transform_image(thumbnail_path, op)

                self.progress.setValue(index)

            self.progress_bar(False, size)
            self.set_curr_section(section_index=-1)

        elif button == self.b_help:
            self.message_box(
                'This GUI is used to align slices to each other. The shortcut commands are as follows: \n\n'
                + '-  `[`: Go back one section. \n' +
                '-  `]`: Go forward one section. \n\n' +
                'Use the buttons on the bottom panel to move', False)

        elif button == self.b_done:
            self.message_box(
                "All selected operations will now be performed on the full sized raw images"
                +
                "This may take an hour or two, depending on how many operations are queued.",
                False)

            # Apply the transformations to the real images
            self.apply_queued_transformations()

            self.sqlController.set_step_completed_in_progress_ini(
                self.stack, '1-4_setup_sorted_filenames')
            self.sqlController.set_step_completed_in_progress_ini(
                self.stack, '1-5_setup_orientations')
            sys.exit(app.exec_())

    def transform_image(self, filename, op):
        def get_last_2d(data):
            if data.ndim <= 2:
                return data
            m, n = data.shape[-2:]
            return data.flat[:m * n].reshape(m, n)

        img = io.imread(filename)
        img = get_last_2d(img)

        # Rotating a multidimensional image has to be done backwards.
        # To rotate right, do np.rot(img, 3), to rotate left, do np.rot(img, 1)
        if op == 'left':
            img = np.rot90(img, 3)
        elif op == 'right':
            img = np.rot90(img, 1)
        elif op == 'flip':
            img = np.flipud(img)
        elif op == 'flop':
            img = np.fliplr(img)

        os.unlink(filename)
        io.imsave(filename, img)
        self.save_to_web_thumbnail(filename, img)

    def save_to_web_thumbnail(self, filename, img):
        filename = os.path.basename(filename)
        png_file = os.path.splitext(filename)[0] + '.png'
        png_path = os.path.join(self.fileLocationManager.thumbnail_web,
                                png_file)
        if os.path.exists(png_path):
            os.unlink(png_path)
        io.imsave(png_path, img)

    def apply_queued_transformations(self):
        pass

    def progress_bar(self, show, max_value):
        if show:
            self.progress.setMaximum(max_value)
            self.progress.show()
        else:
            self.progress.hide()

        self.b_quality.setDisabled(show)
        self.b_move_left.setDisabled(show)
        self.b_move_right.setDisabled(show)
        self.b_flip_vertical.setDisabled(show)
        self.b_flip_horozontal.setDisabled(show)
        self.b_rotate_right.setDisabled(show)
        self.b_rotate_left.setDisabled(show)
        self.b_remove.setDisabled(show)
        self.b_done.setDisabled(show)

    def message_box(self, text, is_warn):
        msg_box = QMessageBox()
        msg_box.setText(text)

        if is_warn:
            msg_box.addButton(QPushButton('Cancel'), QMessageBox.RejectRole)
            msg_box.addButton(QPushButton('No'), QMessageBox.NoRole)
            msg_box.addButton(QPushButton('Yes'), QMessageBox.YesRole)

        return msg_box.exec_()

    def keyPressEvent(self, event):
        try:
            key = event.key()
        except AttributeError:
            key = event

        if key == 91:  # [
            index = self.get_valid_section_index(self.curr_section_index - 1)
            self.set_curr_section(index)
        elif key == 93:  # ]
            index = self.get_valid_section_index(self.curr_section_index + 1)
            self.set_curr_section(index)
        else:
            print(key)

    def closeEvent(self, event):
        sys.exit(app.exec_())