def __init__(self, parent=None): super(ExperimentResultWidget, self).__init__() self.tab = QtGui.QTabWidget() # Figure 1 figure = plt.figure() canvas = FigureCanvas(figure) canvas.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Expanding) canvas.setMinimumSize(50, 50) toolbar = NavigationToolbar(canvas, self) toolbar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum) widget = QtGui.QWidget() layout = QtGui.QVBoxLayout() layout.addWidget(canvas) layout.addWidget(toolbar) widget.setLayout(layout) self.tab.addTab(widget, 'Results') self.plot = figure # Tab 1 self.trackCountsTable = QtGui.QTableWidget() self.tab.addTab(self.trackCountsTable, 'Track Counts') # Tab 2 self.networkOverviewTable = QtGui.QTableWidget() self.tab.addTab(self.networkOverviewTable, 'Network Overview') # Tab 3 self.trackTerminationTable = QtGui.QTableWidget() self.trackInitiationTable = QtGui.QTableWidget() widget = QtGui.QWidget() layout = QtGui.QVBoxLayout() layout.addWidget(self.trackTerminationTable) layout.addWidget(self.trackInitiationTable) widget.setLayout(layout) self.tab.addTab(widget, 'Track Fragmentation') layout = QtGui.QVBoxLayout() layout.addWidget(self.tab) self.setLayout(layout)
class PandasMplWidget(QtGui.QWidget): def __init__(self, parent=None): QtGui.QWidget.__init__(self, parent) self.pmc = PandasMplCanvas(width=2, height=2, dpi=100) self.toolbar = NavigationToolbar(self.pmc.figure.canvas, self) sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Fixed) sizePolicy.setHeightForWidth(True) self.toolbar.setSizePolicy(sizePolicy) vbox = QtGui.QVBoxLayout() vbox.addWidget(self.pmc) vbox.addWidget(self.toolbar) self.setLayout(vbox) def get_figure_canvas(self): return self.pmc def plot(self, data): self.pmc.plot_data_frame(data)
class ThresholdCacheWidget(QtGui.QWidget): def __init__(self, on_changed_ev, parent=None): super(ThresholdCacheWidget, self).__init__() self.on_changed_ev = on_changed_ev self.parent = parent self.histogram_figure = plt.figure() self.histogram_canvas = FigureCanvas(self.histogram_figure) self.histogram_canvas.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Expanding) self.histogram_canvas.setMinimumSize(50, 50) self.histogram_toolbar = NavigationToolbar(self.histogram_canvas, parent) self.histogram_toolbar.coordinates = False self.histogram_toolbar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum) self.image_figure = plt.figure() self.image_canvas = FigureCanvas(self.image_figure) self.image_canvas.setMinimumSize(50, 50) self.image_canvas.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Expanding) self.image_toolbar = NavigationToolbar(self.image_canvas, parent) self.image_toolbar.coordinates = False self.image_toolbar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum) gs = grd.GridSpec(3, 1) self.ax_objects = self.histogram_figure.add_subplot(gs[0, 0]) self.ax_area = self.histogram_figure.add_subplot(gs[1, 0], sharex=self.ax_objects) self.ax_image = self.image_figure.add_subplot(111) layout = QtGui.QGridLayout() q1 = QtGui.QLabel("<b>Choose Threshold</b>") layout.addWidget(q1, 0, 0, 1, 1) layout.addWidget(QtGui.QLabel("Click on either graph to pick a threshold value"), 1, 0, 1, 1) layout.addWidget(self.histogram_canvas, 3, 0, 1, 1) layout.addWidget(self.histogram_toolbar, 4, 0, 1, 1) self.roiSelectorBar = ROISelectorBar(self.image_figure, self.ax_image) self.roiSelectorBar.roi_changed.connect(self.roiSelectorBar_roi_changed) self.calibrationBar = CalibrationBar(10, self.image_figure, self.ax_image) self.calibrationBar.calibration_changed.connect(self.calibrationBar_calibration_changed) q2 = QtGui.QLabel("<b>Define Region of Interest</b>") layout.addWidget(q2, 0, 1, 1, 1) layout.addWidget(self.roiSelectorBar, 1, 1, 1, 1) layout.addWidget(self.calibrationBar, 2, 1, 1, 1) layout.addWidget(self.image_canvas, 3, 1, 1, 1) layout.addWidget(self.image_toolbar, 4, 1, 1, 1) self.setLayout(layout) self.histogram_figure.canvas.mpl_connect('button_press_event', self.on_histogram_button_pressed) # self.image_figure.canvas.mpl_connect('button_press_event', self.on_image_button_pressed) self.thresholds = [] @staticmethod def create_background(impaths): """ create a background image for background subtraction. The background image is the maximum pixel values from three grayscale images. params --------- impaths: (list) this is a sorted list containing paths to all the image files from one recording. """ if len(impaths) == 0: return None first = mpimg.imread(impaths[0]) mid = mpimg.imread(impaths[int(len(impaths)/2)]) last = mpimg.imread(impaths[-1]) return np.maximum(np.maximum(first, mid), last) def clear_experiment_data(self): self.roiSelectorBar.clear_roi() self.threshold = 0.0005 def load_experiment(self, experiment): self.experiment = experiment self.annotation_filename = str(paths.threshold_data(experiment.id)) try: with open(self.annotation_filename, "rt") as f: data = json.loads(f.read()) # self.circle = None self.roiSelectorBar.json_to_data(data) self.threshold = data.get('threshold', 0.0005) except IOError as ex: self.clear_experiment_data() self.calibration_filename = str(paths.calibration_data(experiment.id)) try: with open(self.calibration_filename, "rt") as f: data = json.loads(f.read()) self.calibrationBar.json_to_data(data) except IOError as ex: self.calibrationBar.clear_calibration() times, impaths = zip(*sorted(experiment.image_files.items())) impaths = [str(s) for s in impaths] if times is not None and len(times) > 0: times = [float(t) for t in times] times, impaths = zip(*sorted(zip(times, impaths))) if impaths is None or len(impaths) == 0: self.background = None self.mid_image = None self.plate_distance = None self.roiSelectorBar.set_guess_polygon([]) else: self.background = ThresholdCacheWidget.create_background(impaths) self.im_shape = self.background.shape # shape is (y,x) self.mid_image = mpimg.imread(impaths[int(len(impaths)/2)]) self.plate_distance = PlateDistance(self.background) self.plate_distance.calculate() p = self.plate_distance.polygon(border=settings.ROI_BORDER_OFFSET, corner=settings.ROI_CORNER_OFFSET) p = [(y, x) for x, y in p] self.roiSelectorBar.set_guess_polygon(p) self.calibrationBar.update_plate_distance(self.plate_distance) self.mouse_points = [] QTimer.singleShot(0, self.show_dialog) def show_dialog(self): dlg = CacheThresholdLoadingDialog(self.experiment.id, self.calculate_threshold, self.finished, self.parent) dlg.setModal(True) dlg.exec_() def calculate_threshold(self, callback): self.thresholds = [] for i, t in enumerate(np.linspace(start=0.00001, stop=0.001, num=30)): valid, N, m, s = self.data_from_threshold(t) if valid: self.thresholds.append((t, N, m, s)) callback(0, i / 30.) callback(0, 1) def finished(self): self.update_data(self.thresholds, self.threshold) self.histogram_figure.canvas.draw() def isComplete(self): return self.roiSelectorBar.isComplete() def create_binary_mask(self, img, background, threshold, minsize=100): """ creates a binary array the same size as the image with 1s denoting objects and 0s denoting background. params -------- img: (image ie. numpy array) each pixel denotes greyscale pixel intensities. background: (image ie. numpy array) the background image with maximum pixel intensities (made with create_background) threshold: (float) the threshold value used to create the binary mask after pixel intensities for (background - image) have been calculated. minsize: (int) the fewest allowable pixels for an object. objects with an area containing fewer pixels are removed. """ if img is None or background is None: return None mask = (background - img) > threshold result = morphology.remove_small_objects(mask, minsize) return result def data_from_threshold(self, threshold): if self.mid_image is None: return False, None, None, None mask = self.create_binary_mask(self.mid_image, self.background, threshold=threshold) labels, N = ndimage.label(mask) sizes = [r.area for r in regionprops(labels)] if len(sizes) == 0: return False, None, None, None else: m, s = np.mean(sizes), np.std(sizes) return True, N, m, s @staticmethod def mkdir_p(path): try: os.makedirs(path) except OSError as exc: # Python >2.5 if exc.errno == errno.EEXIST and os.path.isdir(path): pass else: raise def save_roi_data(self): if self.annotation_filename is None: return # note: the image is usually transposed. we didn't here, # so x and y are flipped during saving process. data = self.roiSelectorBar.data_to_json() data['threshold'] = self.threshold data['shape'] = self.im_shape ThresholdCacheWidget.mkdir_p(os.path.dirname(self.annotation_filename)) with open(self.annotation_filename, "wt") as f: f.write(json.dumps(data, indent=4)) def save_calibration_data(self): if self.calibration_filename is None: return data = self.calibrationBar.data_to_json() ThresholdCacheWidget.mkdir_p(os.path.dirname(self.calibration_filename)) with open(self.calibration_filename, "wt") as f: f.write(json.dumps(data, indent=4)) def update_data(self, thresholds, current_threshold): if len(thresholds) == 0: self.ax_objects.clear() self.ax_area.clear() self.ax_image.clear() self.line_objects = None self.line_area = None return x, ns, means, stds = zip(*thresholds) final_t = x[-1] # make the plot self.ax_objects.clear() self.ax_objects.plot(x, ns, '.-', color='black') self.ax_objects.set_ylabel('N Objects') self.ax_objects.set_ylim([0, 150]) top = np.array(means) + np.array(stds) bottom = np.array(means) - np.array(stds) self.ax_area.clear() self.ax_area.plot(x, means, '.-', color='blue', label='mean') self.ax_area.fill_between(x, top, bottom, color='green', alpha=0.5) # self.ax_area.plot(x, top, '--', color='green', label='mean') # self.ax_area.plot(x, bottom, '--', color='green', label='mean - std') self.ax_area.axvline(x=.5, ymin=0, ymax=1) self.ax_area.legend(loc='upper right') self.ax_area.set_ylim([0, 600]) self.ax_area.set_ylabel('Blob Area (pxls)') self.ax_area.set_xlabel('Contrast Threshold') self.ax_objects.set_xlim([0, final_t]) self.line_objects = self.ax_objects.plot((current_threshold, current_threshold), (-10000, 10000), '--', color='red') self.line_area = self.ax_area.plot((current_threshold, current_threshold), (-1000000, 1000000), '--', color='red') self.show_threshold() def show_threshold(self): """ plots an image with the outlines of all objects overlaid on top. params -------- img: (image ie. numpy array) each pixel denotes greyscale pixel intensities. background: (image ie. numpy array) the background image with maximum pixel intensities (made with create_background) threshold: (float) the threshold value used to create the binary mask after pixel intensities for (background - image) have been calculated. """ mask = self.create_binary_mask(self.mid_image, self.background, self.threshold) self.ax_image.clear() self.ax_image.imshow(self.mid_image, cmap=plt.cm.gray, interpolation='nearest') self.ax_image.contour(mask, [0.5], linewidths=1.2, colors='b') self.ax_image.axis('off') self.roiSelectorBar.update_image() self.calibrationBar.update_image() def on_histogram_button_pressed(self, ev): if self.threshold != ev.xdata: self.threshold = ev.xdata if self.line_objects is not None and len(self.line_objects) > 0: self.line_objects[0].remove() if self.line_area is not None and len(self.line_area) > 0: self.line_area[0].remove() self.line_objects = self.ax_objects.plot((self.threshold, self.threshold), (-10000, 10000), '--', color='red') self.line_area = self.ax_area.plot((self.threshold, self.threshold), (-1000000, 1000000), '--', color='red') self.show_threshold() self.histogram_figure.canvas.draw() self.save_roi_data() def roiSelectorBar_roi_changed(self): self.save_roi_data() self.on_changed_ev() def calibrationBar_calibration_changed(self): self.save_calibration_data()