class Prototype(QWidget):
    def __init__(self, parent=None):
        super(Prototype, self).__init__(parent)

        # ================================================================================
        #   Window Setup
        # ================================================================================
        self.setWindowTitle("Prototype Demo")
        self.setWindowFlags(Qt.WindowCloseButtonHint
                            | Qt.WindowMinimizeButtonHint)

        # ================================================================================
        #   Variable Setup
        # ================================================================================
        self.nissl_filename = config.NISSL_DEFAULT_FILE

        # ================================================================================
        #   UI Layout Preparation
        # ================================================================================
        layout_main = QVBoxLayout()  # ***** Vertical Layout (Main)

        # ***** Top Layout (Input Layout + Region Layout)
        layout_main.addLayout(self.get_layout_top())

        # Set the main layout
        self.setLayout(layout_main)

        # ================================================================================
        #   Routines after UI Preparation
        # ================================================================================
        self.refresh_image()  # Set default image

        self.thread_match = MatchingThread(self)
        self.thread_match.startProgress.connect(self.on_thread_match_start)
        self.thread_match.updateProgress.connect(self.on_thread_match_update)
        self.thread_match.endProgress.connect(self.on_thread_match_end)

    ##################################################################################
    #   UI Layout/Widget Routines
    ##################################################################################
    def get_layout_top(self):
        layout = QHBoxLayout()
        layout.addStretch(1)

        layout.addWidget(self.get_widget_input())
        layout.addWidget(self.get_widget_region())

        return layout

    def get_widget_input(self):
        canvas_box = QGroupBox("Input Image")
        layout = QVBoxLayout()

        # *** Button (Open File)
        self.btn_open = QPushButton("Open Nissl file")
        self.btn_open.clicked.connect(self.on_click_btn_open)
        layout.addWidget(self.btn_open)

        # *** Canvas (Input Image)
        self.canvas_input = Graph(self, width=1, height=8, dpi=500)
        self.canvas_input.corners_callback = self.on_canvas_input_corners_update
        layout.addWidget(self.canvas_input)

        canvas_box.setLayout(layout)
        return canvas_box

    def get_widget_region(self):
        canvas_box = QGroupBox("Selected Region")
        layout = QVBoxLayout()

        # ================================================================================
        # ==================== Horizontal Layout (Matches Button + Add Entire Image Button)
        match_layout = QHBoxLayout()

        # *** Button (Find Best Match)
        self.btn_find_match = QPushButton("Find best matches")
        self.btn_find_match.clicked.connect(self.on_click_btn_find_match)
        self.btn_find_match.setEnabled(False)
        match_layout.addWidget(self.btn_find_match)

        self.btn_add_image = QPushButton("Add entire image")
        self.btn_add_image.clicked.connect(self.on_click_btn_add_image)
        match_layout.addWidget(self.btn_add_image)

        layout.addLayout(match_layout)
        # ==================== End Horizontal Layout (Match Layout)
        # ================================================================================

        progress_layout = QHBoxLayout()
        # *** ProgressBar (Matching Completion)
        self.progressbar_match = QProgressBar()
        self.progressbar_match.setValue(0)
        progress_layout.addWidget(self.progressbar_match)

        layout.addLayout(progress_layout)

        # *** Label (Matching Status)
        self.label_match_status = QLabel(
            "Select a region you want to find a match for")
        layout.addWidget(self.label_match_status)

        # *** Canvas (Region Crops)
        self.canvas_region = Graph(self, width=5, height=5, dpi=100)
        self.canvas_region.is_interactive = False
        layout.addWidget(self.canvas_region)

        # *** Label (Ratio Test Distance)
        self.label_slider_ratio_test = QLabel("")
        layout.addWidget(self.label_slider_ratio_test)

        # *** Slider (Ratio Test Distance)
        self.slider_ratio_test = QSlider(Qt.Horizontal)
        self.slider_ratio_test.valueChanged.connect(
            self.on_slider_change_ratio_test)
        self.slider_ratio_test.setMinimum(0)
        self.slider_ratio_test.setMaximum(100)
        self.slider_ratio_test.setValue(int(config.DISTANCE_RATIO * 100))
        self.slider_ratio_test.setTickPosition(QSlider.NoTicks)
        self.slider_ratio_test.setTickInterval(1)
        self.slider_ratio_test.setEnabled(False)
        layout.addWidget(self.slider_ratio_test)

        if (config.UI_ANGLE):
            # *** Label (Angle)
            self.label_angle = QLabel("Angle: 0")
            layout.addWidget(self.label_angle)

            # *** Slider (Angle)
            self.slider_angle = QSlider(Qt.Horizontal)
            self.slider_angle.valueChanged.connect(self.on_slider_change_angle)
            self.slider_angle.setMinimum(0)
            self.slider_angle.setMaximum(360)
            self.slider_angle.setTickPosition(QSlider.NoTicks)
            self.slider_angle.setTickInterval(1)
            self.slider_angle.setEnabled(False)
            layout.addWidget(self.slider_angle)

        if (config.UI_WARP):
            # *** Label (Warp Points)
            self.label_warp_points = QLabel("")
            layout.addWidget(self.label_warp_points)

            # *** Slider (Warp Points)
            self.slider_warp_points = QSlider(Qt.Horizontal)
            self.slider_warp_points.valueChanged.connect(
                self.on_slider_change_warp_points)
            self.slider_warp_points.setMinimum(3)
            self.slider_warp_points.setMaximum(50)
            self.slider_warp_points.setTickPosition(QSlider.NoTicks)
            self.slider_warp_points.setTickInterval(1)
            self.slider_warp_points.setValue(5)
            self.slider_warp_points.setEnabled(False)
            layout.addWidget(self.slider_warp_points)

            # *** Label (Warp Disp Min)
            self.label_warp_disp_min = QLabel("Min Displacement: ")
            layout.addWidget(self.label_warp_disp_min)

            # *** Label (Warp Disp Max)
            self.label_warp_disp_max = QLabel("Max Displacement: ")
            layout.addWidget(self.label_warp_disp_max)

            from qrangeslider import QRangeSlider
            self.slider_warp_disp = QRangeSlider()
            self.slider_warp_disp.setMin(0)
            self.slider_warp_disp.setMax(50)
            self.slider_warp_disp.setRange(1, 5)
            self.slider_warp_disp.setEnabled(False)
            layout.addWidget(self.slider_warp_disp)

            self.btn_warp = QPushButton("Warp")
            self.btn_warp.clicked.connect(self.on_click_btn_warp)
            self.btn_warp.setEnabled(False)
            layout.addWidget(self.btn_warp)

            self.btn_reset = QPushButton("Reset")
            self.btn_reset.clicked.connect(self.on_click_btn_reset)
            self.btn_reset.setEnabled(False)
            layout.addWidget(self.btn_reset)

        canvas_box.setLayout(layout)
        return canvas_box

    ##################################################################################
    #   Slider Events
    ##################################################################################
    def on_slider_change_ratio_test(self):
        new_ratio = float(self.slider_ratio_test.value() / 100.0)
        config.DISTANCE_RATIO = new_ratio
        self.label_slider_ratio_test.setText("Distance Ratio Test: " +
                                             str(config.DISTANCE_RATIO))

    def on_slider_change_angle(self):
        angle = self.slider_angle.value()
        self.label_angle.setText("Angle: " + str(angle))

        if self.canvas_region.im is not None:
            import scipy.misc
            im = scipy.misc.imrotate(self.region, angle)
            self.canvas_region.imshow(im)

    def on_slider_change_warp_points(self):
        points = self.slider_warp_points.value()
        self.label_warp_points.setText("Warp Points: " + str(points))

    def on_slider_change_warp_min(self):
        pass

    def on_slider_change_warp_max(self):
        pass

    ##################################################################################
    #   Class Functions
    ##################################################################################
    def refresh_image(self):
        im = util.im_read(self.nissl_filename)
        self.canvas_input.imshow(im)

    def set_im_region(self, im_region):
        logger.info("Image Region Shape: {0}", im_region.shape)
        w, h, c = im_region.shape

        # Reduce the size of images to a reasonable range
        #import scipy.misc as misc
        #reduction_percent = int(config.RESIZE_WIDTH/w * 100)
        #im_region = misc.imresize(im_region, reduction_percent)
        #logger.info("Resized region to {0}", im_region.shape)

        # Check if we should save the region for testing
        if config.UI_SAVE_REGION:
            util.im_write('region.jpg', im_region)

        # Save the original selection for actual matching
        self.region = im_region

        # Check if the user would like to see the region keypoints
        if config.UI_SHOW_KP:
            kp, des = sift.extract_sift(im_region)
            im_region = cv2.drawKeypoints(
                im_region,
                kp,
                None,
                flags=cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS)

        self.canvas_region.imshow(im_region)
        self.canvas_input.clear_corners()
        self.btn_find_match.setEnabled(True)
        self.slider_ratio_test.setEnabled(True)

        if config.UI_WARP:
            self.btn_warp.setEnabled(True)
            self.btn_reset.setEnabled(True)
            self.slider_warp_points.setEnabled(True)
            self.slider_warp_disp.setEnabled(True)

        if config.UI_ANGLE:
            self.slider_angle.setEnabled(True)

    ##################################################################################
    #   Canvas Events
    ##################################################################################
    def on_canvas_input_corners_update(self):
        count = len(self.canvas_input.corners)

        if count == 4:
            x, y = [], []

            for corner in self.canvas_input.corners:
                # Numpy slicing uses integers
                x.append(corner[0].astype(np.uint64))
                y.append(corner[1].astype(np.uint64))

            x1, x2, y1, y2 = min(x), max(x), min(y), max(y)
            im_region = self.canvas_input.im[y1:y2, x1:x2].copy()
            self.set_im_region(im_region)

        # Redraw corner scatterplot
        return True

    ##################################################################################
    #   Button Events
    ##################################################################################
    def on_click_btn_find_match(self):
        self.thread_match.set_im(self.region)
        self.thread_match.start()

    def on_click_btn_add_image(self):
        self.set_im_region(self.canvas_input.im.copy())

    def on_click_btn_warp(self):
        min_disp = self.slider_warp_disp.getRange()[0]
        max_disp = self.slider_warp_disp.getRange()[1]
        im_warp = warping.warp(self.canvas_region.im, 5, min_disp, max_disp)
        self.canvas_region.imshow(im_warp)

    def on_click_btn_reset(self):
        self.set_im_region(self.region)

    def on_click_btn_open(self):
        new_filename, extra = QFileDialog.getOpenFileName(
            self, 'Open file', config.NISSL_DIR, "Image files (*.jpg *.png)")

        if new_filename:
            self.nissl_filename = new_filename
            self.refresh_image()

    ##################################################################################
    #   Thread Events
    ##################################################################################
    def on_thread_match_start(self, total):
        self.progressbar_match.setMaximum(total)

        self.canvas_input.is_interactive = False

        self.btn_open.setEnabled(False)
        self.btn_find_match.setEnabled(False)
        self.btn_add_image.setEnabled(False)
        self.slider_ratio_test.setEnabled(False)

        if config.UI_ANGLE:
            self.slider_angle.setEnabled(False)

        # Timing
        timing.stopwatch()

    def on_thread_match_update(self, index):
        self.progressbar_match.setValue(index)
        self.label_match_status.setText("Matching with plate " + str(index))

    def on_thread_match_end(self, matches):
        timing.stopwatch("Matching Time: ")
        self.canvas_input.is_interactive = True

        self.btn_open.setEnabled(True)
        self.btn_find_match.setEnabled(True)
        self.btn_add_image.setEnabled(True)
        self.slider_ratio_test.setEnabled(True)

        if config.UI_ANGLE:
            self.slider_angle.setEnabled(True)

        if len(matches) <= 0:
            self.label_match_status.setText("Didn't find a good match.")

        else:
            self.label_match_status.setText("Found " + str(len(matches)) +
                                            " possible matches")
            results_diag = ResultsDialog(self.nissl_filename, matches, self)
            results_diag.show()
示例#2
0
class BinaryPercentileTransformation(Transformation):
    def __init__(self):
        super(BinaryPercentileTransformation).__init__()
        self.col_ids = []
        self.name = "Binary (Percentile)"

    def add_widgets(self, layout, owner, controller):
        suffix_label = QLabel("Column Suffix", owner)
        self.suffix_box = QLineEdit(owner)
        transform_cols_label = QLabel("Columns to transform", owner)
        self.transformation_cols_box = ui_objects.ColumnList(owner, controller)
        perc_label = QLabel("Select quantile range to flag (inclusive)", owner)
        self.perc_slider = QRangeSlider(owner)

        layout.addWidget(suffix_label)
        layout.addWidget(self.suffix_box)
        layout.addWidget(transform_cols_label)
        layout.addWidget(self.transformation_cols_box)
        layout.addWidget(perc_label)
        layout.addWidget(self.perc_slider)

    def add_summary_widgets(self, box, controller):
        box.transformation = self

        box.tran_label.setText("Binary (Percentile)")

        box.lower_perc_label = QLabel("Lower Percentile:", box)
        box.lower_perc_label.setFont(controller.NORMAL_FONT)

        box.lower_perc = QLabel(str(self.lower_perc), box)
        box.lower_perc.setFont(controller.SMALL_FONT)

        box.upper_perc_label = QLabel("Upper Percentile:", box)
        box.upper_perc_label.setFont(controller.NORMAL_FONT)

        box.upper_perc = QLabel(str(self.upper_perc), box)
        box.upper_perc.setFont(controller.SMALL_FONT)

        box.transformation_cols_label = QLabel("Columns transformed:", box)
        box.transformation_cols_label.setFont(controller.NORMAL_FONT)
        box.transformation_cols = QListWidget(box)
        box.transformation_cols.addItems(self.col_ids)

        box.content_layout.addWidget(box.lower_perc_label)
        box.content_layout.addWidget(box.lower_perc)
        box.content_layout.addWidget(box.upper_perc_label)
        box.content_layout.addWidget(box.upper_perc)
        box.content_layout.addWidget(box.transformation_cols_label)
        box.content_layout.addWidget(box.transformation_cols)

        box.setFixedHeight(500)
        box.setFixedWidth(500)

    def validate_inputs(self, owner):
        self.suffix = self.suffix_box.text()
        self.transform_cols = [
            x.text() for x in self.transformation_cols_box.selectedItems()
        ]
        self.lower_perc = self.perc_slider.getRange()[0]
        self.upper_perc = self.perc_slider.getRange()[1]
        if self.suffix == "":
            QMessageBox.about(
                owner, "Warning",
                "Please provide a valid suffix for the resulting column(s).")
            return False
        if len(self.transform_cols) == 0:
            QMessageBox.about(
                owner, "Warning",
                "Please select at least one column to transform.")
            return False
        return True

    def create_transformation(self, df):
        for col in self.transform_cols:
            col_name = self.generate_column_name(df, col + "_" + self.suffix)
            self.col_ids.append(col_name)

            numeric_col = pd.to_numeric(df[col], errors="coerce")

            lower_q = np.nanquantile(numeric_col, self.lower_perc / 100)
            upper_q = np.nanquantile(numeric_col, self.upper_perc / 100)

            new_col = np.where(
                np.logical_and(numeric_col >= lower_q, numeric_col <= upper_q),
                1, 0)
            new_col = np.where(new_col == np.nan, np.nan, new_col)
            df[col_name] = new_col

        return df
class RangesliderZmq(QtGui.QWidget):
    def __init__(self):
        #app = QtGui.QApplication(sys.argv)
        super().__init__()
        self.gesture_dict = GestureDict()
        self.port = 5556
        logging.basicConfig(level=logging.DEBUG, format='%(message)s')
        self.initUI()
        #sys.exit(app.exec_())

    def initUI(self):
        port = self.port
        self.create_socket(port)
        range_label = QtGui.QLabel('events')
        self.range_events = QRangeSlider()
        self.range_events.show()
        self.range_events.setFixedWidth(300)
        self.range_events.setFixedHeight(36)
        self.range_events.setMin(0)
        self.range_events.setMax(4)
        self.range_events.setRange(0, 1)
        self.range_events.startValueChanged.connect(
            lambda: self.keep_slider_min(self.range_events))
        hbox_events = QtGui.QHBoxLayout()
        hbox_events.addWidget(range_label)
        hbox_events.addWidget(self.range_events)
        self.textbox = QtGui.QLineEdit()
        self.update_btn = QtGui.QPushButton("update")
        self.update_btn.clicked.connect(lambda: self.button_click(port))
        self.update_btn.setFixedWidth(100)
        hbox = QtGui.QHBoxLayout()
        hbox.addWidget(self.update_btn)

        magnitude_label = QtGui.QLabel('magnitude in g/10')
        self.range_magnitude = QRangeSlider()
        self.range_magnitude.show()
        self.range_magnitude.setFixedWidth(300)
        self.range_magnitude.setFixedHeight(36)
        self.range_magnitude.setMin(20)
        self.range_magnitude.setMax(80)
        self.range_magnitude.setRange(20, 30)

        hbox_magnitude = QtGui.QHBoxLayout()
        hbox_magnitude.addWidget(magnitude_label)
        hbox_magnitude.addWidget(self.range_magnitude)
        self.filter_length = QRangeSlider()
        self.filter_length.show()
        self.filter_length.setFixedWidth(300)
        self.filter_length.setFixedHeight(36)
        self.filter_length.setMin(0)
        self.filter_length.setMax(250)
        self.filter_length.setRange(0, 100)
        self.filter_length.startValueChanged.connect(
            lambda: self.keep_slider_min(self.filter_length))
        filter_length_label = QtGui.QLabel('filter length in samples')
        hbox_length = QtGui.QHBoxLayout()
        hbox_length.addWidget(filter_length_label)
        hbox_length.addWidget(self.filter_length)
        self.message_label = QtGui.QLabel("messages will be here")
        self.exit_btn = QtGui.QPushButton('exit')
        self.exit_btn.clicked.connect(
            lambda: self.exit_click(self.socket, self.context, port))
        vbox = QtGui.QVBoxLayout()
        vbox.addStretch(1)
        vbox.addLayout(hbox_events)
        vbox.addLayout(hbox_magnitude)
        vbox.addLayout(hbox_length)
        vbox.addWidget(self.message_label)
        vbox.addWidget(self.update_btn)
        vbox.addLayout(hbox)
        vbox.addWidget(self.exit_btn)
        self.setLayout(vbox)
        self.setGeometry(300, 300, 300, 150)
        self.setWindowTitle('rangesliders')
        self.show()

    @QtCore.pyqtSlot()
    def button_click(self, port):
        ''' handle button click event '''
        try:
            self.update_gesture_dict()
            message = self.gesture_dict.make_json()
            logging.info('rangeslider sending message {}'.format(message))
            self.socket.send_json(message, flags=zmq.NOBLOCK)
        except zmq.error.Again as e:
            logging.info('no receiver for the message: {}'.format(e))
            # restart the socket if nothing to receive the message
            # if receiver closed, the socket needs to be restarted
            self.close_socket()
            self.create_socket(port)

    def close_socket(self):
        ''' close the socket and context '''
        self.socket.close()
        self.context.term()

    def create_socket(self, port):
        ''' create a socket using pyzmq with PAIR context '''
        self.context = zmq.Context()
        self.socket = self.context.socket(zmq.PAIR)
        self.socket.setsockopt(zmq.LINGER, 0)
        self.socket.bind("tcp://*:%s" % port)
        stream_pair = zmqstream.ZMQStream(self.socket)
        stream_pair.on_recv(self.process_message)

    def exit_click(self, socket, context, port):
        ''' handle exit button click '''
        socket.close()
        context.term()
        sys.exit()

    def filter_length_change(self):
        ''' filter length slider has changed '''
        length = self.filter_length.value()
        self.set_message_label('filter_length: {}'.format(length))

    def keep_slider_min(self, slider):
        ''' keep the slider length minimum as one '''
        try:
            slider.setStart(0)
        except RuntimeError as e:
            pass
        self.set_message_label('cannot change this')

    def process_message(self, msg):
        time = datetime.now()
        time = time.strftime('%H:%M:%S')
        text = ('{}: {}'.format(time, msg))
        self.message_label.setText(text)

    def set_message_label(self, text):
        self.message_label.setText(text)

    def timer_timeout(self):
        ''' handle the QTimer timeout '''
        try:
            msg = self.socket.recv(flags=zmq.NOBLOCK).decode()
            self.process_message(msg)
        except zmq.error.Again as e:
            return

    def update_gesture_dict(self):
        ''' get status of all the rangesliders into GestureDict object '''
        magnitude_min, magnitude_max = self.range_magnitude.getRange()
        filter_length = self.filter_length.end()
        events = self.range_events.end()
        self.gesture_dict.update_dict(magnitude_min=magnitude_min, \
            magnitude_max=magnitude_max/10, events=events, \
            filter_length=filter_length)