class Window(QtGui.QDialog): def __init__(self, parent=None): super(Window, self).__init__(parent) self.figure = plt.figure() self.canvas = FigureCanvas(self.figure) self.toolbar = NavigationToolbar(self.canvas, self) # self.toolbar.hide() # Just some button self.button = QtGui.QPushButton('Plot') self.button.clicked.connect(self.plot) self.button1 = QtGui.QPushButton('Zoom') self.button1.clicked.connect(self.zoom) self.button2 = QtGui.QPushButton('Pan') self.button2.clicked.connect(self.pan) self.button3 = QtGui.QPushButton('Home') self.button3.clicked.connect(self.home) # set the layout layout = QtGui.QVBoxLayout() layout.addWidget(self.toolbar) layout.addWidget(self.canvas) layout.addWidget(self.button) layout.addWidget(self.button1) layout.addWidget(self.button2) layout.addWidget(self.button3) self.setLayout(layout) def home(self): self.toolbar.home() def zoom(self): self.toolbar.zoom() def pan(self): self.toolbar.pan() def plot(self): ''' plot some random stuff ''' data = [random.random() for i in range(25)] ax = self.figure.add_subplot(211) ax.hold(False) ax.plot(data, '*-') bx = self.figure.add_subplot(212) bx.hold(False) bx.plot(data, '*-') self.canvas.draw()
class MPlotWidget(QtGui.QWidget): def __init__(self, parent=None): super(MPlotWidget, self).__init__(parent) self.figure = plt.figure() self.canvas = FigureCanvas(self.figure) self.toolbar = NavigationToolbar(self.canvas, self) self.toolbar.hide() self.plotbutton = QtGui.QPushButton('Plot') self.zoombutton = QtGui.QPushButton('Zoom') self.zoombutton.clicked.connect(self.zoom) self.panbutton = QtGui.QPushButton('Pan') self.panbutton.clicked.connect(self.pan) self.homebutton = QtGui.QPushButton('Home') self.homebutton.clicked.connect(self.home) self.savebutton = QtGui.QPushButton('Save') self.savebutton.clicked.connect(self.save) layout = QtGui.QVBoxLayout() layout.addWidget(self.toolbar) layout.addWidget(self.canvas) buttonbox = QtGui.QHBoxLayout() buttonbox.addWidget(self.plotbutton) buttonbox.addWidget(self.zoombutton) buttonbox.addWidget(self.panbutton) buttonbox.addWidget(self.homebutton) buttonbox.addWidget(self.savebutton) layout.addLayout(buttonbox) self.setLayout(layout) self.ax = self.figure.add_subplot(111) self.ax.hold(False) def home(self): self.toolbar.home() def zoom(self): self.toolbar.zoom() def pan(self): self.toolbar.pan() def save(self): self.figure.savefig('1.png') """
class Window(QtGui.QDialog): def __init__(self, parent=None): super(Window, self).__init__(parent) self.figure = plt.figure() self.canvas = FigureCanvas(self.figure) self.toolbar = NavigationToolbar(self.canvas, self) self.toolbar.hide() # Just some button self.button = QtGui.QPushButton('Plot') self.button.clicked.connect(self.plot) self.button1 = QtGui.QPushButton('Zoom') self.button1.clicked.connect(self.zoom) self.button2 = QtGui.QPushButton('Pan') self.button2.clicked.connect(self.pan) self.button3 = QtGui.QPushButton('Home') self.button3.clicked.connect(self.home) # set the layout layout = QtGui.QVBoxLayout() layout.addWidget(self.toolbar) layout.addWidget(self.canvas) layout.addWidget(self.button) layout.addWidget(self.button1) layout.addWidget(self.button2) layout.addWidget(self.button3) self.setLayout(layout) def home(self): self.toolbar.home() def zoom(self): self.toolbar.zoom() def pan(self): self.toolbar.pan() def plot(self): ''' plot some random stuff ''' data = [random.random() for i in range(25)] ax = self.figure.add_subplot(111) ax.hold(False) ax.plot(data, '*-') self.canvas.draw()
def zoom(self, *args): """ Turn on/off zoom (zoom button) :param args: :return: """ NavigationToolbar2.zoom(self, args) if self._navigationMode == MyNavigationToolbar.NAVIGATION_MODE_ZOOM: # out of zoom mode self._navigationMode = MyNavigationToolbar.NAVIGATION_MODE_NONE else: # into zoom mode self._navigationMode = MyNavigationToolbar.NAVIGATION_MODE_ZOOM return
def zoom( self, *args ): self.resetActionsState( self.zoomAction ) NavigationToolbar2QTAgg.zoom( self, *args )
class calibrlogger(PyQt4.QtGui.QMainWindow, Calibr_Ui_Dialog): # An instance of the class Calibr_Ui_Dialog is created same time as instance of calibrlogger is created def __init__(self, parent, settingsdict1={}, obsid=''): PyQt4.QtGui.QApplication.setOverrideCursor(QCursor(Qt.WaitCursor))#show the user this may take a long time... self.obsid = obsid self.log_pos = None self.y_pos = None self.meas_ts = None self.head_ts = None self.level_masl_ts = None self.loggerpos_masl_or_offset_state = 1 self.settingsdict = settingsdict1 PyQt4.QtGui.QDialog.__init__(self, parent) self.setAttribute(PyQt4.QtCore.Qt.WA_DeleteOnClose) self.setupUi(self) # Required by Qt4 to initialize the UI self.setWindowTitle("Calibrate logger") # Set the title for the dialog self.connect(self.pushButton, PyQt4.QtCore.SIGNAL("clicked()"), self.calibrateandplot) self.INFO.setText("Select the observation point with logger data to be calibrated.") self.log_calc_manual.setText("<a href=\"https://sites.google.com/site/midvattenpluginforqgis/usage/3-edit-data?pli=1#TOC-Calibrate-water-level-measurements-from-data-logger-\">Midvatten manual</a>") # Create a plot window with one single subplot self.calibrplotfigure = plt.figure() self.axes = self.calibrplotfigure.add_subplot( 111 ) self.canvas = FigureCanvas( self.calibrplotfigure ) self.mpltoolbar = NavigationToolbar( self.canvas, self.widgetPlot ) lstActions = self.mpltoolbar.actions() self.mpltoolbar.removeAction( lstActions[ 7 ] ) self.layoutplot.addWidget( self.canvas ) self.layoutplot.addWidget( self.mpltoolbar ) self.show() self.cid =[] self.connect(self.pushButtonFrom, PyQt4.QtCore.SIGNAL("clicked()"), self.set_from_date_from_x) self.connect(self.pushButtonTo, PyQt4.QtCore.SIGNAL("clicked()"), self.set_to_date_from_x) self.connect(self.pushButtonupdateplot, PyQt4.QtCore.SIGNAL("clicked()"), self.update_plot) self.connect(self.loggerpos_masl_or_offset, PyQt4.QtCore.SIGNAL("clicked()"), self.loggerpos_masl_or_offset_change) self.connect(self.pushButtonLpos, PyQt4.QtCore.SIGNAL("clicked()"), self.calibrate_from_plot_selection) self.connect(self.pushButtonCalcBestFit, PyQt4.QtCore.SIGNAL("clicked()"), self.calc_best_fit) self.connect(self.pushButton_delete_logger, PyQt4.QtCore.SIGNAL("clicked()"), lambda: self.delete_selected_range(u'w_levels_logger')) self.connect(self.pushButton_delete_meas, PyQt4.QtCore.SIGNAL("clicked()"), lambda: self.delete_selected_range(u'w_levels')) self.get_tolerance() # Populate combobox with obsid from table w_levels_logger self.load_obsid_from_db() PyQt4.QtGui.QApplication.restoreOverrideCursor()#now this long process is done and the cursor is back as normal def load_obsid_from_db(self): self.combobox_obsid.clear() myconnection = utils.dbconnection() if myconnection.connect2db() == True: # skapa en cursor curs = myconnection.conn.cursor() rs=curs.execute("""select distinct obsid from w_levels_logger order by obsid""") self.combobox_obsid.addItem('') for row in curs: self.combobox_obsid.addItem(row[0]) rs.close() myconnection.closedb() def load_obsid_and_init(self): """ Checks the current obsid and reloads all ts. :return: obsid Info: Before, some time series was only reloaded when the obsid was changed, but this caused a problem if the data was changed in the background in for example spatialite gui. Now all time series are reloaded always. It's rather fast anyway. """ obsid = unicode(self.combobox_obsid.currentText()) if not obsid: utils.pop_up_info("ERROR: no obsid is chosen") meas_sql = r"""SELECT date_time, level_masl FROM w_levels WHERE obsid = '""" + obsid + """' ORDER BY date_time""" self.meas_ts = self.sql_into_recarray(meas_sql) head_sql = r"""SELECT date_time as 'date [datetime]', head_cm / 100 FROM w_levels_logger WHERE obsid = '""" + obsid + """' ORDER BY date_time""" self.head_ts = self.sql_into_recarray(head_sql) self.obsid = obsid level_masl_ts_sql = r"""SELECT date_time as 'date [datetime]', level_masl FROM w_levels_logger WHERE obsid = '""" + self.obsid + """' ORDER BY date_time""" self.level_masl_ts = self.sql_into_recarray(level_masl_ts_sql) return obsid def getlastcalibration(self): obsid = self.load_obsid_and_init() if not obsid=='': sql = """SELECT MAX(date_time), loggerpos FROM (SELECT date_time, (level_masl - (head_cm/100)) as loggerpos FROM w_levels_logger WHERE level_masl > -990 AND obsid = '""" sql += obsid sql += """')""" self.lastcalibr = utils.sql_load_fr_db(sql)[1] if self.lastcalibr[0][1] and self.lastcalibr[0][0]: text = """Last pos. for logger in """ text += obsid text += """\nwas """ + str(self.lastcalibr[0][1]) + """ masl\nat """ + str(self.lastcalibr[0][0]) else: text = """There is no earlier known\nposition for the logger\nin """ + unicode(self.combobox_obsid.currentText())#self.obsid[0] self.INFO.setText(text) def calibrateandplot(self): obsid = self.load_obsid_and_init() if not self.LoggerPos.text() == '': self.calibrate() self.update_plot() # def calibrate(self, fr_d_t=self.FromDateTime.dateTime().toPyDateTime(), to_d_t=self.ToDateTime.dateTime().toPyDateTime()): def calibrate(self): self.calib_help.setText("Calibrating") PyQt4.QtGui.QApplication.setOverrideCursor(PyQt4.QtCore.Qt.WaitCursor) obsid = self.load_obsid_and_init() if not obsid=='': sanity1sql = """select count(obsid) from w_levels_logger where obsid = '""" + obsid[0] + """'""" sanity2sql = """select count(obsid) from w_levels_logger where head_cm not null and head_cm !='' and obsid = '""" + obsid[0] + """'""" if utils.sql_load_fr_db(sanity1sql)[1] == utils.sql_load_fr_db(sanity2sql)[1]: # This must only be done if head_cm exists for all data fr_d_t = self.FromDateTime.dateTime().toPyDateTime() to_d_t = self.ToDateTime.dateTime().toPyDateTime() if self.loggerpos_masl_or_offset_state == 1: self.update_level_masl_from_head(obsid, fr_d_t, to_d_t, self.LoggerPos.text()) else: self.update_level_masl_from_level_masl(obsid, fr_d_t, to_d_t, self.LoggerPos.text()) self.getlastcalibration() else: utils.pop_up_info("Calibration aborted!!\nThere must not be empty cells or\nnull values in the 'head_cm' column!") else: self.INFO.setText("Select the observation point with logger data to be calibrated.") self.calib_help.setText("") PyQt4.QtGui.QApplication.restoreOverrideCursor() def update_level_masl_from_level_masl(self, obsid, fr_d_t, to_d_t, newzref): """ Updates the level masl using newzref :param obsid: (str) The obsid :param fr_d_t: (datetime) start of calibration :param to_d_t: (datetime) end of calibration :param newzref: (int/float/str [m]) The correction that should be made against the head [m] :return: None """ sql =r"""UPDATE w_levels_logger SET level_masl = """ sql += str(newzref) sql += """ + level_masl WHERE obsid = '""" sql += obsid # Sqlite seems to have problems with date comparison date_time >= a_date, so they have to be converted into total seconds first. sql += """' AND CAST(strftime('%s', date_time) AS NUMERIC) >= """ sql += str((fr_d_t - datetime.datetime(1970,1,1)).total_seconds()) sql += """ AND CAST(strftime('%s', date_time) AS NUMERIC) <= """ sql += str((to_d_t - datetime.datetime(1970,1,1)).total_seconds()) sql += """ """ dummy = utils.sql_alter_db(sql) def update_level_masl_from_head(self, obsid, fr_d_t, to_d_t, newzref): """ Updates the level masl using newzref :param obsid: (str) The obsid :param fr_d_t: (datetime) start of calibration :param to_d_t: (datetime) end of calibration :param newzref: (int/float/str [m]) The correction that should be made against the head [m] :return: None """ sql =r"""UPDATE w_levels_logger SET level_masl = """ sql += str(newzref) sql += """ + head_cm / 100 WHERE obsid = '""" sql += obsid # Sqlite seems to have problems with date comparison date_time >= a_date, so they have to be converted into total seconds first. sql += """' AND CAST(strftime('%s', date_time) AS NUMERIC) >= """ sql += str((fr_d_t - datetime.datetime(1970,1,1)).total_seconds()) sql += """ AND CAST(strftime('%s', date_time) AS NUMERIC) <= """ sql += str((to_d_t - datetime.datetime(1970,1,1)).total_seconds()) sql += """ """ dummy = utils.sql_alter_db(sql) def sql_into_recarray(self, sql): """ Converts and runs an sql-string and turns the answer into an np.recarray and returns it""" my_format = [('date_time', datetime.datetime), ('values', float)] #Define (with help from function datetime) a good format for numpy array recs = utils.sql_load_fr_db(sql)[1] table = np.array(recs, dtype=my_format) #NDARRAY table2=table.view(np.recarray) # RECARRAY Makes the two columns inte callable objects, i.e. write table2.values return table2 def update_plot(self): """ Plots self.level_masl_ts, self.meas_ts and maybe self.head_ts """ self.reset_plot_selects_and_calib_help() self.calib_help.setText("Updating plot") PyQt4.QtGui.QApplication.setOverrideCursor(PyQt4.QtCore.Qt.WaitCursor) obsid = self.load_obsid_and_init() self.axes.clear() p=[None]*2 # List for plot objects # Load manual reading (full time series) for the obsid self.plot_recarray(self.axes, self.meas_ts, obsid, 'o-', 10) # Load Loggerlevels (full time series) for the obsid if self.loggerLineNodes.isChecked(): logger_line_style = '.-' else: logger_line_style = '-' self.plot_recarray(self.axes, self.level_masl_ts, obsid + unicode(' logger', 'utf-8'), logger_line_style, 10) #Plot the original head_cm if self.plot_logger_head.isChecked(): self.plot_recarray(self.axes, self.head_ts, obsid + unicode(' original logger head', 'utf-8'), logger_line_style, 10) """ Finish plot """ self.axes.grid(True) self.axes.yaxis.set_major_formatter(tick.ScalarFormatter(useOffset=False, useMathText=False)) self.calibrplotfigure.autofmt_xdate() self.axes.set_ylabel(unicode('Level (masl)', 'utf-8')) #This is the method that accepts even national characters ('åäö') in matplotlib axes labels self.axes.set_title(unicode('Calibration plot for ', 'utf-8') + str(obsid)) #This is the method that accepts even national characters ('åäö') in matplotlib axes labels for label in self.axes.xaxis.get_ticklabels(): label.set_fontsize(10) for label in self.axes.yaxis.get_ticklabels(): label.set_fontsize(10) #plt.show() self.canvas.draw() plt.close(self.calibrplotfigure)#this closes reference to self.calibrplotfigure PyQt4.QtGui.QApplication.restoreOverrideCursor() self.calib_help.setText("") def plot_recarray(self, axes, a_recarray, lable, line_style, picker=10): """ Plots a recarray to the supplied axes object """ # Get help from function datestr2num to get date and time into float myTimestring = [a_recarray.date_time[idx] for idx in xrange(len(a_recarray))] numtime=datestr2num(myTimestring) #conv list of strings to numpy.ndarray of floats axes.plot_date(numtime, a_recarray.values, line_style, label=lable, picker=picker) def set_from_date_from_x(self): """ Used to set the self.FromDateTime by clicking on a line node in the plot self.canvas """ self.reset_plot_selects_and_calib_help() self.calib_help.setText("Select a node to use as \"from\"") self.deactivate_pan_zoom() self.canvas.setFocusPolicy(Qt.ClickFocus) self.canvas.setFocus() self.cid.append(self.canvas.mpl_connect('pick_event', lambda event: self.set_date_from_x_onclick(event, self.FromDateTime))) def set_to_date_from_x(self): """ Used to set the self.ToDateTime by clicking on a line node in the plot self.canvas """ self.reset_plot_selects_and_calib_help() self.calib_help.setText("Select a node to use as \"to\"") self.deactivate_pan_zoom() self.canvas.setFocusPolicy(Qt.ClickFocus) self.canvas.setFocus() self.cid.append(self.canvas.mpl_connect('pick_event', lambda event: self.set_date_from_x_onclick(event, self.ToDateTime))) def set_date_from_x_onclick(self, event, date_holder): """ Sets the date_holder to a date from a line node closest to the pick event date_holder: a QDateTimeEdit object. """ found_date = utils.find_nearest_date_from_event(event) date_holder.setDateTime(found_date) self.reset_plot_selects_and_calib_help() def reset_plot_selects_and_calib_help(self): """ Reset self.cid and self.calib_help """ self.reset_cid() self.log_pos = None self.y_pos = None self.calib_help.setText("") def reset_cid(self): """ Resets self.cid to an empty list and disconnects unused events """ for x in self.cid: self.canvas.mpl_disconnect(x) self.cid = [] def calibrate_from_plot_selection(self): """ Calibrates by selecting a line node and a y-position on the plot The user have to click on the button three times and follow instructions. The process: 1. Selecting a line node. 2. Selecting a selecting a y-position from the plot. 3. Extracting the head from head_ts with the same date as the line node. 4. Calculating y-position - head (or level_masl) and setting self.LoggerPos. 5. Run calibration. """ #Run init to make sure self.meas_ts and self.head_ts is updated for the current obsid. self.load_obsid_and_init() self.deactivate_pan_zoom() self.canvas.setFocusPolicy(Qt.ClickFocus) self.canvas.setFocus() if self.log_pos is None: self.calib_help.setText("Select a logger node.") self.cid.append(self.canvas.mpl_connect('pick_event', self.set_log_pos_from_node_date_click)) if self.log_pos is not None and self.y_pos is None: self.calib_help.setText("Select a y position to move to.") self.cid.append(self.canvas.mpl_connect('button_press_event', self.set_y_pos_from_y_click)) if self.log_pos is not None and self.y_pos is not None: PyQt4.QtGui.QApplication.setOverrideCursor(PyQt4.QtCore.Qt.WaitCursor) if self.loggerpos_masl_or_offset_state == 1: logger_ts = self.head_ts else: logger_ts = self.level_masl_ts y_pos = self.y_pos log_pos = self.log_pos self.y_pos = None self.log_pos = None log_pos_date = datestring_to_date(log_pos).replace(tzinfo=None) logger_value = None #Get the value for the selected node for idx, date_value_tuple in enumerate(logger_ts): raw_date, logger_value = date_value_tuple date = datestring_to_date(raw_date).replace(tzinfo=None) if date == log_pos_date: break if logger_value is None: utils.pop_up_info("No connection between head_ts dates and logger date could be made!\nTry again or choose a new logger line node!") else: self.LoggerPos.setText(str(float(y_pos) - float(logger_value))) PyQt4.QtGui.QApplication.restoreOverrideCursor() self.calibrateandplot() self.calib_help.setText("") def set_log_pos_from_node_date_click(self, event): """ Sets self.log_pos variable to the date (x-axis) from the node nearest the pick event """ found_date = utils.find_nearest_date_from_event(event) self.calib_help.setText("Logger node " + str(found_date) + " selected, click button \"Calibrate by selection in plot\" again.") self.log_pos = found_date self.reset_cid() def set_y_pos_from_y_click(self, event): """ Sets the self.y_pos variable to the y value of the click event """ self.y_pos = event.ydata self.calib_help.setText("Y position set, click button \"Calibrate by selection in plot\" again for calibration.") self.reset_cid() def calc_best_fit(self): """ Calculates the self.LoggerPos from self.meas_ts and self.head_ts First matches measurements from self.meas_ts to logger values from self.head_ts. This is done by making a mean of all logger values inside self.meas_ts date - tolerance and self.meas_ts date + tolerance. (this could probably be change to get only the closest logger value inside the tolerance instead) (Tolerance is gotten from self.get_tolerance()) Then calculates the mean of all matches and set to self.LoggerPos. """ obsid = self.load_obsid_and_init() self.reset_plot_selects_and_calib_help() tolerance = self.get_tolerance() really_calibrate_question = utils.askuser("YesNo", """This will calibrate all values inside the chosen period\nusing the mean difference between logger values and measurements.\n\nTime tolerance for matching logger and measurement nodes set to '""" + ' '.join(tolerance) + """'\n\nContinue?""") if really_calibrate_question.result == 0: # if the user wants to abort return PyQt4.QtGui.QApplication.setOverrideCursor(PyQt4.QtCore.Qt.WaitCursor) if self.loggerpos_masl_or_offset_state == 1: logger_ts = self.head_ts else: logger_ts = self.level_masl_ts coupled_vals = self.match_ts_values(self.meas_ts, logger_ts, tolerance) if not coupled_vals: utils.pop_up_info("There was no matched measurements or logger values inside the chosen period.\n Try to increase the tolerance!") else: self.LoggerPos.setText(str(utils.calc_mean_diff(coupled_vals))) self.calibrateandplot() PyQt4.QtGui.QApplication.restoreOverrideCursor() def match_ts_values(self, meas_ts, logger_ts, tolerance): """ Matches two timeseries values for shared timesteps For every measurement point, a mean of logger values inside measurementpoint + x minutes to measurementpoint - x minutes is coupled together. At the first used measurement, only logger values greater than the set start date is used. At the last measurement, only logger values lesser than the set end date is used. This is done so that values from another logger reposition is not mixed with the chosen logger positioning. (Hard to explain). """ coupled_vals = [] #Get the tolerance, default to 10 minutes tol = int(tolerance[0]) tol_period = tolerance[1] logger_gen = utils.ts_gen(logger_ts) try: l = next(logger_gen) except StopIteration: return None log_vals = [] all_done = False #The .replace(tzinfo=None) is used to remove info about timezone. Needed for the comparisons. This should not be a problem though as the date scale in the plot is based on the dates from the database. outer_begin = self.FromDateTime.dateTime().toPyDateTime().replace(tzinfo=None) outer_end = self.ToDateTime.dateTime().toPyDateTime().replace(tzinfo=None) logger_step = datestring_to_date(l[0]).replace(tzinfo=None) for m in meas_ts: if logger_step is None: break meas_step = datestring_to_date(m[0]).replace(tzinfo=None) step_begin = dateshift(meas_step, -tol, tol_period) step_end = dateshift(meas_step, tol, tol_period) if step_end < outer_begin: continue if step_begin > outer_end: break #Skip logger steps that are earlier than the chosen begin date or are not inside the measurement period. while logger_step < step_begin or logger_step < outer_begin: try: l = next(logger_gen) except StopIteration: all_done = True break logger_step = datestring_to_date(l[0]).replace(tzinfo=None) log_vals = [] while logger_step is not None and logger_step <= step_end and logger_step <= outer_end: if not math.isnan(float(l[1])) or l[1] == 'nan' or l[1] == 'NULL': log_vals.append(float(l[1])) try: l = next(logger_gen) except StopIteration: all_done = True break logger_step = datestring_to_date(l[0]).replace(tzinfo=None) if log_vals: mean = np.mean(log_vals) if not math.isnan(mean): coupled_vals.append((m[1], mean)) if all_done: break return coupled_vals def get_tolerance(self): """ Get the period tolerance, default to 10 minutes """ if not self.bestFitTolerance.text(): tol = '10 minutes' self.bestFitTolerance.setText(tol) else: tol = self.bestFitTolerance.text() tol_splitted = tol.split() if len(tol_splitted) != 2: utils.pop_up_info("Must write time resolution also, ex. 10 minutes") return tuple(tol_splitted) def loggerpos_masl_or_offset_change(self): if self.loggerpos_masl_or_offset_state == 1: self.label_11.setText("Offset relative to calibrated values:") self.loggerpos_masl_or_offset.setText("Change to logger position") self.label_adjustment_info.setText("Adjustments made from calibrated values") self.loggerpos_masl_or_offset_state = 0 else: self.label_11.setText("Logger position, masl:") self.loggerpos_masl_or_offset.setText("Change to offset") self.label_adjustment_info.setText("Adjustments made from head") self.loggerpos_masl_or_offset_state = 1 def deactivate_pan_zoom(self): """ Deactivates the NavigationToolbar pan or zoom feature if they are currently active """ if self.mpltoolbar._active == "PAN": self.mpltoolbar.pan() elif self.mpltoolbar._active == "ZOOM": self.mpltoolbar.zoom() def delete_selected_range(self, table_name): """ Deletes the current selected range from the database from w_levels_logger :return: De """ current_loaded_obsid = self.obsid selected_obsid = self.load_obsid_and_init() if current_loaded_obsid != selected_obsid: utils.pop_up_info("Error!\n The obsid selection has been changed but the plot has not been updated. No deletion done.\nUpdating plot.") self.update_plot() return fr_d_t = str((self.FromDateTime.dateTime().toPyDateTime() - datetime.datetime(1970,1,1)).total_seconds()) to_d_t = str((self.ToDateTime.dateTime().toPyDateTime() - datetime.datetime(1970,1,1)).total_seconds()) sql_list = [] sql_list.append(r"""delete from "%s" """%table_name) sql_list.append(r"""where obsid = '%s' """%selected_obsid) sql_list.append(r"""AND CAST(strftime('%s', date_time) AS NUMERIC) """) sql_list.append(r""" >= '%s' """%fr_d_t) sql_list.append(r"""AND CAST(strftime('%s', date_time) AS NUMERIC) """) sql_list.append(r""" <= '%s' """%to_d_t) sql = ''.join(sql_list) really_delete = utils.askuser("YesNo", "Do you want to delete the period " + str(self.FromDateTime.dateTime().toPyDateTime()) + " to " + str(self.ToDateTime.dateTime().toPyDateTime()) + " for obsid " + selected_obsid + " from table " + table_name + "?").result if really_delete: utils.sql_alter_db(sql) self.update_plot()
class Window(QtGui.QDialog): def __init__(self, parent=None): super(Window, self).__init__(parent) self.figure = plt.figure() self.canvas = FigureCanvas(self.figure) self.canvas.setSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Expanding) self.toolbar = NavigationToolbar(self.canvas, self) self.toolbar.hide() # Timestream info self.timestreamLabel = QtGui.QLabel('Time-stream root path:') self.timestreamText = QtGui.QLineEdit('') self.timestreamText.setSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Fixed) self.timestreamDateLabel = QtGui.QLabel('Start date (yyyy_mm_dd_hh_mm_ss):') self.timestreamDateText = QtGui.QLineEdit('') self.timestreamDateText.setSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Fixed) self.timestreamTimeLabel = QtGui.QLabel('Time interval (seconds):') self.timestreamTimeText = QtGui.QLineEdit('') self.timestreamTimeText.setSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Fixed) self.initStreamTimeButton = QtGui.QPushButton('&Initialise timestream by time') self.initStreamTimeButton.clicked.connect(self.initialiseTimestreamByTime) self.initStreamFileButton = QtGui.QPushButton('&Initialise timestream by files') self.initStreamFileButton.clicked.connect(self.initialiseTimestreamByFiles) # Image loading and processing self.loadImageButton = QtGui.QPushButton('&Load (next) image') self.loadImageButton.clicked.connect(self.loadImage) self.rotateImageButton = QtGui.QPushButton('&Rotate 90-deg') self.rotateImageButton.clicked.connect(self.rotateImage90Degrees) self.slider = QtGui.QSlider(QtCore.Qt.Horizontal) self.slider.setFocusPolicy(QtCore.Qt.StrongFocus) self.slider.setSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Fixed) self.slider.setTickPosition(QtGui.QSlider.TicksBothSides) self.slider.setMinimum(-16) self.slider.setMaximum(16) self.slider.setValue(0) self.slider.setTickInterval(4) self.slider.setSingleStep(1) self.slider.valueChanged.connect(self.rotateSmallAngle) self.applySmallRotationButton = QtGui.QPushButton('&Apply') self.applySmallRotationButton.clicked.connect(self.applySmallRotation) self.loadCamCalibButton = QtGui.QPushButton('Load &cam. param.') self.loadCamCalibButton.clicked.connect(self.loadCamCalib) self.colorcardRadioButton = QtGui.QRadioButton('Select color car&d') self.colorcardRadioButton.setChecked(False) self.colorcardRadioButton.clicked.connect(self.selectWhat) self.trayRadioButton = QtGui.QRadioButton('Select &tray') self.trayRadioButton.setChecked(False) self.trayRadioButton.clicked.connect(self.selectWhat) self.trayRoundCheckBox = QtGui.QCheckBox('Round') self.trayRoundCheckBox.setChecked(True) self.potRadioButton = QtGui.QRadioButton('Select &pot') self.potRadioButton.setChecked(False) self.potRadioButton.clicked.connect(self.selectWhat) self.zoomButton = QtGui.QPushButton('&Zoom') self.zoomButton.setCheckable(True) self.zoomButton.clicked.connect(self.zoom) self.panButton = QtGui.QPushButton('&Pan') self.panButton.setCheckable(True) self.panButton.clicked.connect(self.pan) self.homeButton = QtGui.QPushButton('&Home') self.homeButton.clicked.connect(self.home) self.correctColorButton = QtGui.QPushButton('Correct colo&r') self.correctColorButton.clicked.connect(self.correctColor) self.save2PipelineButton = QtGui.QPushButton('&Save as pipeline settings') self.save2PipelineButton.clicked.connect(self.savePipelineSettings) self.testPipelineButton = QtGui.QPushButton('Test &pipeline processing') self.testPipelineButton.clicked.connect(self.testPipeline) self.status = QtGui.QTextEdit('') self.status.setSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Expanding) self.mousePosition = QtGui.QLabel('') # set the layout layout = QtGui.QHBoxLayout() rightWidget = QtGui.QWidget() buttonlayout = QtGui.QVBoxLayout(rightWidget) buttonlayout.addWidget(self.timestreamLabel) buttonlayout.addWidget(self.timestreamText) buttonlayout.addWidget(self.timestreamDateLabel) buttonlayout.addWidget(self.timestreamDateText) buttonlayout.addWidget(self.timestreamTimeLabel) buttonlayout.addWidget(self.timestreamTimeText) buttonlayout.addWidget(self.initStreamTimeButton) buttonlayout.addWidget(self.initStreamFileButton) buttonlayout.addWidget(self.loadImageButton) buttonlayout.addWidget(self.loadCamCalibButton) buttonlayout.addWidget(self.rotateImageButton) layoutSmallRotation = QtGui.QHBoxLayout() layoutSmallRotation.addWidget(self.slider) layoutSmallRotation.addWidget(self.applySmallRotationButton) buttonlayout.addLayout(layoutSmallRotation) buttonlayout.addWidget(self.colorcardRadioButton) layoutTrayShape = QtGui.QHBoxLayout() layoutTrayShape.addWidget(self.trayRadioButton) layoutTrayShape.addWidget(self.trayRoundCheckBox) buttonlayout.addLayout(layoutTrayShape) buttonlayout.addWidget(self.potRadioButton) buttonlayout.addWidget(self.zoomButton) buttonlayout.addWidget(self.panButton) buttonlayout.addWidget(self.homeButton) buttonlayout.addWidget(self.correctColorButton) buttonlayout.addWidget(self.save2PipelineButton) buttonlayout.addWidget(self.testPipelineButton) buttonlayout.addWidget(self.status) buttonlayout.addWidget(self.mousePosition) rightWidget.setMaximumWidth(250) leftLayout = QtGui.QVBoxLayout() leftLayout.addWidget(self.toolbar) leftLayout.addWidget(self.canvas) layout.addWidget(rightWidget) layout.addLayout(leftLayout) self.setLayout(layout) self.group = QtGui.QButtonGroup() self.group.addButton(self.colorcardRadioButton) self.group.addButton(self.trayRadioButton) self.group.addButton(self.potRadioButton) self.loadPreviousInputs() self.panMode = False self.zoomMode = False self.ts = None self.tsImages = None self.ax = None self.plotRect = None self.plotImg = None self.image = None self.potTemplate = None self.UndistMapX = None self.UndistMapY = None self.trayAspectRatio = 0.835 self.colorcardAspectRatio = 1.5 self.potAspectRatio = 1.0 self.leftClicks = [] self.colorcardParams = None self.scriptPath = os.path.dirname(os.path.realpath(__file__)) self.timestreamRootPath = None self.pl = None # Ouput parameters self.ImageSize = None self.colorcardList = [] self.trayList = [] self.potList = [] self.rotationAngle = 0.0 self.smaleRotationAngle = 0.0 self.CameraMatrix = None self.DistCoefs = None self.isDistortionCorrected = False self.settingFileName = None def selectWhat(self): if self.trayRadioButton.isChecked(): self.status.append('Start selecting tray.') elif self.colorcardRadioButton.isChecked(): self.status.append('Start selecting color bar.') else: self.status.append('Start selecting pot.') def home(self): self.toolbar.home() def zoom(self): self.toolbar.zoom() if not self.zoomMode: self.zoomMode = True self.panMode = False self.panButton.setChecked(False) else: self.zoomMode = False def pan(self): self.toolbar.pan() if not self.panMode: self.panMode = True self.zoomMode = False self.zoomButton.setChecked(False) else: self.panMode = False def initialiseTimestreamByTime(self): self.timestreamRootPath = str(self.timestreamText.text()) if len(self.timestreamRootPath) > 0: self.status.append('Initialise a timestream at ' + str(self.timestreamRootPath)) self.ts = timestream.TimeStream() self.ts.load(self.timestreamRootPath) self.status.append('Done') startDate = None timeInterval = None date = str(self.timestreamDateText.text()) if len(date) > 0: startDate = timestream.parse.ts_parse_date(date) time = str(self.timestreamTimeText.text()) if len(time) > 0: timeInterval = int(eval(time)) self.tsImages = self.ts.iter_by_timepoints(start = startDate, interval = timeInterval, remove_gaps=False) self.loadImage() else: self.status.append('Please provide timestream root path.') def initialiseTimestreamByFiles(self): self.timestreamRootPath = str(self.timestreamText.text()) if len(self.timestreamRootPath) > 0: self.status.append('Initialise a timestream at ' + str(self.timestreamRootPath)) self.ts = timestream.TimeStream() self.ts.load(self.timestreamRootPath) self.status.append('Done') self.tsImages = self.ts.iter_by_files() self.loadImage() else: self.status.append('Please provide timestream root path.') def loadImage(self): ''' load and show an image''' if self.tsImages != None: try: tsImage = self.tsImages.next() if tsImage.pixels == np.array([]): self.status.append('Missing image.') except: tsImage.pixels == np.array([]) self.status.append('There is no more images.') if tsImage.pixels == np.array([]): self.image = None self.updateFigure() return self.image = tsImage.pixels fname = tsImage.path else: fname = QtGui.QFileDialog.getOpenFileName(self, 'Open image', '/mnt/phenocam/a_data/TimeStreams/Borevitz/BVZ0036/BVZ0036-GC02L-C01~fullres-orig/2014/2014_06/2014_06_24/2014_06_24_08/') app.processEvents() if len(fname) == 0: return self.image = cv2.imread(str(fname))[:,:,::-1] self.status.append('Loaded image from ' + str(fname)) # reset all outputs # self.colorcardList = [] # self.trayList = [] # self.potList = [] self.isDistortionCorrected = False if self.rotationAngle != None: self.image = cd.rotateImage(self.image, self.rotationAngle + self.smaleRotationAngle) # Undistort image if mapping available if not self.isDistortionCorrected and self.UndistMapX != None and self.UndistMapY != None: self.image = cv2.remap(self.image.astype(np.uint8), self.UndistMapX, self.UndistMapY, cv2.INTER_CUBIC) self.isDistortionCorrected = True self.updateFigure() def changeCursor(self, event): # cursor = Cursor(self.ax, useblit=True, color='red', linewidth=1) self.canvas.setCursor(QtGui.QCursor(QtCore.Qt.CrossCursor )) def updateFigure(self, image = None, resetFigure = False): if self.image != None: if image == None: image = self.image if self.ax == None: self.ax = self.figure.add_subplot(111) self.ax.figure.canvas.mpl_connect('button_press_event', self.onMouseClicked) self.ax.figure.canvas.mpl_connect('motion_notify_event', self.onMouseMoves) self.ax.figure.canvas.mpl_connect('figure_enter_event', self.changeCursor) self.ax.hold(False) if self.plotImg == None or resetFigure: self.plotImg = self.ax.imshow(image) else: self.plotImg.set_data(image) self.figure.tight_layout() xs, ys = [], [] for Rect in self.colorcardList: tl, bl, br, tr = Rect xs = xs + [tl[0], bl[0], br[0], tr[0], tl[0], np.nan] ys = ys + [tl[1], bl[1], br[1], tr[1], tl[1], np.nan] for Rect in self.trayList: tl, bl, br, tr = Rect xs = xs + [tl[0], bl[0], br[0], tr[0], tl[0], np.nan] ys = ys + [tl[1], bl[1], br[1], tr[1], tl[1], np.nan] for Rect in self.potList: tl, bl, br, tr = Rect xs = xs + [tl[0], bl[0], br[0], tr[0], tl[0], np.nan] ys = ys + [tl[1], bl[1], br[1], tr[1], tl[1], np.nan] for x,y in self.leftClicks: xs = xs + [x] ys = ys + [y] # if self.crosshair != None: # xs = xs + [np.nan, 0, self.image.shape[1], np.nan, self.crosshair[0], self.crosshair[0], np.nan] # ys = ys + [np.nan, self.crosshair[1], self.crosshair[1], np.nan, 0, self.image.shape[0], np.nan] if len(xs) > 0 and len(ys) > 0: if self.plotRect == None: self.ax.hold(True) self.plotRect, = self.ax.plot(xs, ys, 'b') self.ax.hold(False) self.ax.set_xlim([0,self.image.shape[1]]) self.ax.set_ylim([0,self.image.shape[0]]) self.ax.invert_yaxis() else: self.plotRect.set_data(xs, ys) self.canvas.draw() app.processEvents() def loadCamCalib(self): ''' load camera calibration image and show an image''' calibPath = os.path.join(self.scriptPath, './data') CalibFile = QtGui.QFileDialog.getOpenFileName(self, 'Open image', calibPath) self.ImageSize, SquareSize, self.CameraMatrix, self.DistCoefs, RVecs, TVecs = cd.readCalibration(CalibFile) self.status.append('Loaded camera parameters from ' + CalibFile) print('CameraMatrix =', self.CameraMatrix) print('DistCoefs =', self.DistCoefs) self.UndistMapX, self.UndistMapY = cv2.initUndistortRectifyMap(self.CameraMatrix, self.DistCoefs, \ None, self.CameraMatrix, self.ImageSize, cv2.CV_32FC1) if self.image != None: self.image = cv2.remap(self.image.astype(np.uint8), self.UndistMapX, self.UndistMapY, cv2.INTER_CUBIC) self.isDistortionCorrected = True self.status.append('Corrected image distortion.') self.updateFigure() # def loadPotTemplate(self): # ''' load pot template image''' # fname = QtGui.QFileDialog.getOpenFileName(self, 'Open image', '/home/chuong/Workspace/traitcapture-bin/unwarp_rectify/data') # self.status.append('Loading image...') # app.processEvents() # self.potTemplate = cv2.imread(str(fname))[:,:,::-1] # if len(self.potList) > 0: def correctColor(self): if self.colorcardParams == None: if len(self.colorcardList) > 0: medianSize = cd.getMedianRectSize(self.colorcardList) capturedColorcards = cd.rectifyRectImages(self.image, self.colorcardList, medianSize) self.colorcardColors, _ = cd.getColorcardColors(capturedColorcards[0], GridSize = [6, 4]) self.colorcardParams = cd.estimateColorParameters(cd.CameraTrax_24ColorCard, self.colorcardColors) else: self.status.append('Need to select a color card first.') return colorMatrix, colorConstant, colorGamma = self.colorcardParams self.imageCorrected = cd.correctColorVectorised(self.image.astype(np.float), colorMatrix, colorConstant, colorGamma) self.imageCorrected[np.where(self.imageCorrected < 0)] = 0 self.imageCorrected[np.where(self.imageCorrected > 255)] = 255 self.imageCorrected = self.imageCorrected.astype(np.uint8) self.updateFigure(self.imageCorrected) def savePipelineSettings(self): ''' save to pipeline setting file''' self.settingFileName = str(QtGui.QFileDialog.getSaveFileName(self, \ 'Save selection (default in ./_data)', self.timestreamRootPath)) if len(self.settingFileName) == 0: return self.settingPath = os.path.relpath(os.path.dirname(self.settingFileName), self.timestreamRootPath) self.settings = {} if self.ImageSize != None and self.CameraMatrix != None and self.DistCoefs != None: undistortDic = {'cameraMatrix': self.CameraMatrix.tolist(), \ 'distortCoefs': self.DistCoefs.tolist(), \ 'imageSize': list(self.ImageSize), 'rotationAngle': self.rotationAngle + self.smaleRotationAngle } else: undistortDic = {} self.settings['undistort'] = undistortDic if len(self.colorcardList) > 0: medianSize = cd.getMedianRectSize(self.colorcardList) capturedColorcards = cd.rectifyRectImages(self.image, self.colorcardList, medianSize) colorCardFile = 'CapturedColorcard.png' cv2.imwrite(os.path.join(self.timestreamRootPath, self.settingPath, colorCardFile), capturedColorcards[0][:,:,::-1].astype(np.uint8)) colorcardColors, colorStd = cd.getColorcardColors(capturedColorcards[0], GridSize = [6,4]) colorcardPosition, Width, Height, Angle = cd.getRectangleParamters(self.colorcardList[0]) colorcardDic = {'colorcardFile': colorCardFile,\ 'colorcardPosition': colorcardPosition.tolist(),\ 'colorcardTrueColors': cd.CameraTrax_24ColorCard,\ 'settingPath': '_data' } else: colorcardDic = {} self.settings['colorcarddetect'] = colorcardDic self.settings['colorcorrect'] = {'minIntensity': 15} if len(self.trayList) > 0: trayMedianSize = cd.getMedianRectSize(self.trayList) trayImages = cd.rectifyRectImages(self.image, self.trayList, trayMedianSize) trayDict = {} trayDict['settingPath'] = '_data' trayDict['trayNumber'] = len(self.trayList) trayDict['trayFiles'] = 'Tray_%02d.png' trayPositions = [] for i,tray in enumerate(trayImages): cv2.imwrite(os.path.join(self.timestreamRootPath, self.settingPath, trayDict['trayFiles'] %i), tray[:,:,::-1].astype(np.uint8)) trayPosition, Width, Height, Angle = cd.getRectangleParamters(self.trayList[i]) trayPositions.append(trayPosition.tolist()) trayDict['trayPositions'] = trayPositions else: trayDict = {} self.settings['traydetect'] = trayDict if len(self.potList) > 0: trayMedianSize = cd.getMedianRectSize(self.trayList) potPosition, Width, Height, Angle = cd.getRectangleParamters(self.potList[0]) Width, Height = int(Width), int(Height) topLeft = [int(self.potList[0][0][0]), int(self.potList[0][0][1])] self.potImage = self.image[topLeft[1]:topLeft[1]+Height, topLeft[0]:topLeft[0]+Width, :] potFile = 'Pot.png' cv2.imwrite(os.path.join(self.timestreamRootPath, self.settingPath, potFile), self.potImage[:,:,::-1].astype(np.uint8)) potDict = {} potDict['potPositions'] = potPosition.tolist() potDict['potSize'] = [int(Width), int(Height)] potDict['traySize'] = [int(trayMedianSize[0]), int(trayMedianSize[1])] potDict['potFile'] = potFile potDict['potTemplateFile'] = 'PotTemplate.png' potDict['settingPath'] = '_data' potTemplatePathIn = os.path.join(self.scriptPath, './data/PotTemplate.png') potTemplatePathOut = os.path.join(self.timestreamRootPath, self.settingPath, potDict['potTemplateFile']) shutil.copyfile(potTemplatePathIn, potTemplatePathOut) if self.potTemplate != None: potTemplateFile = 'potTemplate.png' cv2.imwrite(os.path.join(self.timestreamRootPath, self.settingPath, potTemplateFile), self.potTemplate[:,:,::-1]) potDict['potTemplateFile'] = potTemplateFile else: potDict = {} self.settings['potdetect'] = potDict self.settings['plantextract'] = {'meth': 'method1', 'methargs': {'threshold' : 0.6, 'kSize' : 5, 'blobMinSize' : 50} } with open(self.settingFileName, 'w') as outfile: outfile.write( yaml.dump(self.settings, default_flow_style=None) ) self.status.append('Saved initial data to ' + self.settingFileName) def testPipeline(self): ''' try running processing pipeline based on user inputs''' if self.tsImages != None: # load setting file if missing if self.settingFileName == None: settingFileName = str(QtGui.QFileDialog.getOpenFileName(self, 'Open pipeline setting file', self.ts.path)) if len(settingFileName) > 0: self.settingFileName = settingFileName else: return # initialise pipeline if self.pl == None: f = file(self.settingFileName) self.settings = yaml.load(f) self.ts.data["settings"] = self.settings self.pl = pipeline.ImagePipeline(self.settings) # process only from undistortion to pot detection self.pl.pipeline = self.pl.pipeline[:5] # read next image instant of pipeline try: tsImage = self.tsImages.next() if tsImage == None: self.status.append('Missing image.') return except: tsImage = None self.status.append('There is no more images.') return self.status.append('Processing ' + tsImage.path) context = {"rts":self.ts, "wts":None, "img":tsImage} result = self.pl.process(context, [tsImage]) self.image, potPosList2 = result potSize = self.settings[4][1]["potSize"] self.potList = [] for potPosList in potPosList2: if potPosList == None: continue for potPos in potPosList: tl = [potPos[0] - potSize[0]//2, potPos[1] - potSize[1]//2] bl = [potPos[0] - potSize[0]//2, potPos[1] + potSize[1]//2] br = [potPos[0] + potSize[0]//2, potPos[1] + potSize[1]//2] tr = [potPos[0] + potSize[0]//2, potPos[1] - potSize[1]//2] self.potList.append([tl, bl, br, tr]) self.updateFigure() self.status.append('Done') else: self.status.append('Pipeline or setting file is missing.') def rotateImage90Degrees(self): if self.image == None: self.status.append('No image to rotate.') return self.rotationAngle = self.rotationAngle + 90 if self.rotationAngle >= 360: self.rotationAngle = self.rotationAngle - 360 self.image = np.rot90(self.image) #.astype(uint8) self.status.append('Rot. angle = %d deg' %self.rotationAngle) self.updateFigure(resetFigure = True) def rotateSmallAngle(self, value): self.smaleRotationAngle = float(value)/4.0 if self.image != None: self.rotatedImage = cd.rotateImage(self.image, self.smaleRotationAngle) self.updateFigure(self.rotatedImage) self.status.append('Rot. angle = %f deg' %(self.rotationAngle + self.smaleRotationAngle)) def applySmallRotation(self): if self.image != None: self.image = cd.rotateImage(self.image, self.smaleRotationAngle) self.updateFigure() self.status.append('Apply small angle rotation') def onMouseClicked(self, event): if self.panMode or self.zoomMode: return print('click', event.button, event.xdata, event.ydata) if event.button == 1 and event.xdata != None and event.ydata != None: self.leftClicks.append([event.xdata, event.ydata]) print('self.leftClicks =', self.leftClicks) Rect = [] AspectRatio = None if self.trayRadioButton.isChecked(): AspectRatio = self.trayAspectRatio elif self.colorcardRadioButton.isChecked(): AspectRatio = self.colorcardAspectRatio elif self.potRadioButton.isChecked(): AspectRatio = self.potAspectRatio if len(self.leftClicks) == 2 and AspectRatio != None: if self.colorcardRadioButton.isChecked(): Rect = cd.getRectCornersFrom2Points(self.image, self.leftClicks, AspectRatio) if self.trayRadioButton.isChecked(): Rect = cd.getRectCornersFrom2Points(self.image, self.leftClicks, AspectRatio, Rounded = self.trayRoundCheckBox.isChecked()) elif self.potRadioButton.isChecked(): Rect = cd.getRectCornersFrom2Points(self.image, self.leftClicks, AspectRatio, Rounded = True) self.leftClicks = [] elif len(self.leftClicks) == 4: Rect = [[x,y] for x,y in self.leftClicks] Rect = cd.correctPointOrder(Rect) self.leftClicks = [] if len(Rect) > 0: if self.trayRadioButton.isChecked(): self.trayList.append(Rect) self.status.append('Added tray selection.') elif self.colorcardRadioButton.isChecked(): self.colorcardList.append(Rect) self.status.append('Added color card selection.') else: self.potList.append(Rect) self.status.append('Added pot selection.') self.updateFigure() elif event.button == 3: # remove the last selection if len(self.leftClicks) > 0: self.leftClicks = self.leftClicks[:-1] self.status.append('Removed a previous click') else: if self.trayRadioButton.isChecked() and len(self.trayList) > 0: self.trayList = self.trayList[:-1] self.status.append('Removed a previous tray selection') elif self.colorcardRadioButton.isChecked() and len(self.colorcardList) > 0: self.colorcardList = self.colorcardList[:-1] self.status.append('Removed a previous color card selection.') elif self.potRadioButton.isChecked() and len(self.potList) > 0: self.potList = self.potList[:-1] self.status.append('Removed a previous pot selection') self.updateFigure() else: print('Ignored click') def onMouseMoves(self, event): if event.inaxes == self.ax: self.mousePosition.setText('x=%d, y=%d' %(event.xdata, event.ydata)) # self.crosshair = [event.xdata, event.ydata] else: self.mousePosition.setText('') # self.crosshair = None # self.updateFigure() def keyPressEvent(self, e): if e.key() == QtCore.Qt.Key_Escape: self.close() def closeEvent(self, event): quit_msg = "Are you sure you want to exit the program?" reply = QtGui.QMessageBox.question(self, 'Message', quit_msg, QtGui.QMessageBox.Yes, QtGui.QMessageBox.No) if reply == QtGui.QMessageBox.Yes: self.saveInputs() event.accept() else: event.ignore() def saveInputs(self): with open('./.inputs.yml', 'w') as myfile: dicInputs = {'rootPath': str(self.timestreamText.text()), 'startDate': str(self.timestreamDateText.text()), 'timeInterval': str(self.timestreamTimeText.text()), } myfile.write( yaml.dump(dicInputs, default_flow_style=None) ) def loadPreviousInputs(self): try: with open('./.inputs.yml', 'r') as myfile: dicInputs = yaml.load(myfile) self.timestreamText.setText(dicInputs['rootPath']) self.timestreamDateText.setText(dicInputs['startDate']) self.timestreamTimeText.setText(dicInputs['timeInterval']) except: pass
def zoom(self, *args) : print 'Zoom is clicked' NavigationToolbar.zoom(self) fig = self.canvas.figure if fig.ntbZoomIsOn : fig.ntbZoomIsOn = False else : fig.ntbZoomIsOn = True
def zoom(self, *args) : print 'Zoom is clicked' NavigationToolbar.zoom(self)
def zoom(self, *args): self.resetActionsState(self.zoomAction) NavigationToolbar2QTAgg.zoom(self, *args)
def zoom(self, *args): print 'Zoom is clicked' NavigationToolbar.zoom(self)
class Window(QtGui.QDialog): def __init__(self, parent=None): super(Window, self).__init__(parent) self.figure = plt.figure() self.canvas = FigureCanvas(self.figure) self.canvas.setSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Expanding) self.toolbar = NavigationToolbar(self.canvas, self) self.toolbar.hide() # Just some button self.colorcardRadioButton = QtGui.QRadioButton('Select color car&d') self.colorcardRadioButton.setChecked(False) self.colorcardRadioButton.clicked.connect(self.selectWhat) self.trayRadioButton = QtGui.QRadioButton('Select &tray') self.trayRadioButton.setChecked(False) self.trayRadioButton.clicked.connect(self.selectWhat) self.potRadioButton = QtGui.QRadioButton('Select &pot') self.potRadioButton.setChecked(False) self.potRadioButton.clicked.connect(self.selectWhat) self.loadImageButton = QtGui.QPushButton('&Load image') self.loadImageButton.clicked.connect(self.loadImage) self.rotateImageButton = QtGui.QPushButton('&Rotate 90-deg') self.rotateImageButton.clicked.connect(self.rotateImage90Degrees) self.loadCamCalibButton = QtGui.QPushButton('Load &cam. param.') self.loadCamCalibButton.clicked.connect(self.loadCamCalib) self.saveGeometriesButton = QtGui.QPushButton('&Save selected geometries') self.saveGeometriesButton.clicked.connect(self.saveSelectedGeometries) self.saveTraysButton = QtGui.QPushButton('&Save selected tray images') self.saveTraysButton.clicked.connect(self.saveSelectedTrayImages) self.saveColorcadButton = QtGui.QPushButton('&Save sel. col. card images') self.saveColorcadButton.clicked.connect(self.saveSelectedColorcardImages) self.save2PipelineButton = QtGui.QPushButton('&Save as pipeline settings') self.save2PipelineButton.clicked.connect(self.savePipelineSettings) self.zoomButton = QtGui.QPushButton('&Zoom') self.zoomButton.setCheckable(True) self.zoomButton.clicked.connect(self.zoom) self.panButton = QtGui.QPushButton('&Pan') self.panButton.setCheckable(True) self.panButton.clicked.connect(self.pan) self.homeButton = QtGui.QPushButton('&Home') self.homeButton.clicked.connect(self.home) self.status = QtGui.QTextEdit('') self.status.setSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Expanding) self.mousePosition = QtGui.QLabel('') # set the layout layout = QtGui.QHBoxLayout() rightWidget = QtGui.QWidget() buttonlayout = QtGui.QVBoxLayout(rightWidget) buttonlayout.addWidget(self.loadImageButton) buttonlayout.addWidget(self.rotateImageButton) buttonlayout.addWidget(self.loadCamCalibButton) buttonlayout.addWidget(self.colorcardRadioButton) buttonlayout.addWidget(self.trayRadioButton) buttonlayout.addWidget(self.potRadioButton) buttonlayout.addWidget(self.zoomButton) buttonlayout.addWidget(self.panButton) buttonlayout.addWidget(self.homeButton) buttonlayout.addWidget(self.saveGeometriesButton) buttonlayout.addWidget(self.saveColorcadButton) buttonlayout.addWidget(self.saveTraysButton) buttonlayout.addWidget(self.save2PipelineButton) buttonlayout.addWidget(self.status) buttonlayout.addWidget(self.mousePosition) rightWidget.setMaximumWidth(200) leftLayout = QtGui.QVBoxLayout() leftLayout.addWidget(self.toolbar) leftLayout.addWidget(self.canvas) layout.addWidget(rightWidget) layout.addLayout(leftLayout) self.setLayout(layout) self.group = QtGui.QButtonGroup() self.group.addButton(self.colorcardRadioButton) self.group.addButton(self.trayRadioButton) self.group.addButton(self.potRadioButton) self.panMode = False self.zoomMode = False self.ax = None self.plotRect = None self.plotImg = None self.image = None self.potTemplate = None self.UndistMapX = None self.UndistMapY = None self.trayAspectRatio = 0.835 self.colorcardAspectRatio = 1.5 self.potAspectRatio = 1.0 self.leftClicks = [] self.ImageSize = None self.CameraMatrix = None self.DistCoefs = None # # change cursor shape # self.setCursor(QtGui.QCursor(QtCore.Qt.CrossCursor )) # Ouput parameters self.colorcardList = [] self.trayList = [] self.potList = [] self.rotationAngle = 0 self.isDistortionCorrected = False def selectWhat(self): if self.trayRadioButton.isChecked(): self.status.append('Start selecting tray.') elif self.colorcardRadioButton.isChecked(): self.status.append('Start selecting color bar.') else: self.status.append('Start selecting pot.') def home(self): self.toolbar.home() def zoom(self): self.toolbar.zoom() if not self.zoomMode: self.zoomMode = True self.panMode = False self.panButton.setChecked(False) else: self.zoomMode = False def pan(self): self.toolbar.pan() if not self.panMode: self.panMode = True self.zoomMode = False self.zoomButton.setChecked(False) else: self.panMode = False def loadImage(self): ''' load and show an image''' fname = QtGui.QFileDialog.getOpenFileName(self, 'Open image', '/mnt/phenocam/a_data/TimeStreams/BorevitzTest/BVZ0036/BVZ0036-GC02L~fullres-orig/2014/2014_06/2014_06_20/2014_06_20_12') self.status.append('Loading image...') app.processEvents() self.image = cv2.imread(str(fname))[:,:,::-1] self.status.append('Loaded image from ' + str(fname)) # reset all outputs self.colorcardList = [] self.trayList = [] self.potList = [] self.rotationAngle = 0 self.isDistortionCorrected = False # Undistort image if mapping available if not self.isDistortionCorrected and self.UndistMapX != None and self.UndistMapY != None: self.image = cv2.remap(self.image.astype(np.uint8), self.UndistMapX, self.UndistMapY, cv2.INTER_CUBIC) self.isDistortionCorrected = True if self.image != None: if self.ax == None: self.ax = self.figure.add_subplot(111) self.ax.figure.canvas.mpl_connect('button_press_event', self.onMouseClicked) self.ax.figure.canvas.mpl_connect('motion_notify_event', self.onMouseMoves) self.ax.figure.canvas.mpl_connect('figure_enter_event', self.changeCursor) self.ax.hold(False) if self.plotImg == None: self.plotImg = self.ax.imshow(self.image) else: self.plotImg.set_data(self.image) self.figure.tight_layout() self.canvas.draw() def changeCursor(self, event): # cursor = Cursor(self.ax, useblit=True, color='red', linewidth=1) self.canvas.setCursor(QtGui.QCursor(QtCore.Qt.CrossCursor )) def updateFigure(self): xs, ys = [], [] for Rect in self.colorcardList: tl, bl, br, tr = Rect xs = xs + [tl[0], bl[0], br[0], tr[0], tl[0], np.nan] ys = ys + [tl[1], bl[1], br[1], tr[1], tl[1], np.nan] for Rect in self.trayList: tl, bl, br, tr = Rect xs = xs + [tl[0], bl[0], br[0], tr[0], tl[0], np.nan] ys = ys + [tl[1], bl[1], br[1], tr[1], tl[1], np.nan] for Rect in self.potList: tl, bl, br, tr = Rect xs = xs + [tl[0], bl[0], br[0], tr[0], tl[0], np.nan] ys = ys + [tl[1], bl[1], br[1], tr[1], tl[1], np.nan] for x,y in self.leftClicks: xs = xs + [x] ys = ys + [y] # if self.crosshair != None: # xs = xs + [np.nan, 0, self.image.shape[1], np.nan, self.crosshair[0], self.crosshair[0], np.nan] # ys = ys + [np.nan, self.crosshair[1], self.crosshair[1], np.nan, 0, self.image.shape[0], np.nan] if len(xs) > 0 and len(ys) > 0: if self.plotRect == None: self.ax.hold(True) self.plotRect, = self.ax.plot(xs, ys, 'b') self.ax.hold(False) self.ax.set_xlim([0,self.image.shape[1]]) self.ax.set_ylim([0,self.image.shape[0]]) self.ax.invert_yaxis() else: self.plotRect.set_data(xs, ys) self.canvas.draw() app.processEvents() def loadCamCalib(self): ''' load camera calibration image and show an image''' CalibFile = QtGui.QFileDialog.getOpenFileName(self, 'Open image', '/home/chuong/Workspace/traitcapture-bin/unwarp_rectify/data') self.ImageSize, SquareSize, self.CameraMatrix, self.DistCoefs, RVecs, TVecs = utils.readCalibration(CalibFile) self.status.append('Loaded camera parameters from ' + CalibFile) print('CameraMatrix =', self.CameraMatrix) print('DistCoefs =', self.DistCoefs) self.UndistMapX, self.UndistMapY = cv2.initUndistortRectifyMap(self.CameraMatrix, self.DistCoefs, \ None, self.CameraMatrix, self.ImageSize, cv2.CV_32FC1) if self.image != None: self.image = cv2.remap(self.image.astype(np.uint8), self.UndistMapX, self.UndistMapY, cv2.INTER_CUBIC) self.isDistortionCorrected = True self.status.append('Corrected image distortion.') if self.plotImg == None: self.plotImg = self.ax.imshow(self.image) else: self.plotImg.set_data(self.image) self.canvas.draw() # def loadPotTemplate(self): # ''' load pot template image''' # fname = QtGui.QFileDialog.getOpenFileName(self, 'Open image', '/home/chuong/Workspace/traitcapture-bin/unwarp_rectify/data') # self.status.append('Loading image...') # app.processEvents() # self.potTemplate = cv2.imread(str(fname))[:,:,::-1] # if len(self.potList) > 0: def saveSelectedGeometries(self): ''' save selected geometries''' fname = QtGui.QFileDialog.getSaveFileName(self, 'Save selected geometries', '/home/chuong/Workspace/traitcapture-bin/unwarp_rectify/data') colorcardList2 = [] for colorcard in self.colorcardList: colorcardList2 = colorcardList2 + colorcard trayList2 = [] for tray in self.trayList: trayList2 = trayList2 + tray potList2 = [] for pot in self.potList: potList2 = potList2 + pot dicdata = {'colorcardself.crosshair = NoneList':np.asarray(colorcardList2), \ 'trayList':np.asarray(trayList2), \ 'potList':np.asarray(potList2), \ 'rotationAngle':self.rotationAngle, \ 'distortionCorrected':int(self.isDistortionCorrected)} cv2yml.dic2yml(fname, dicdata) self.status.append('Saved selected geometries to ' + fname) def saveSelectedTrayImages(self): ''' save selected trays''' fname = QtGui.QFileDialog.getSaveFileName(self, 'Save selected tray images', '/home/chuong/Workspace/traitcapture-bin/unwarp_rectify/data') medianWidth, medianHeight = utils.getMedianRectSize(self.trayList) rectifiedTrayImages = utils.rectifyRectImages(self.image, self.trayList, MedianSize = [medianWidth, medianHeight]) for i,rectifiedImage in enumerate(rectifiedTrayImages): cv2.imwrite(str(fname) %i, rectifiedImage) def saveSelectedColorcardImages(self): ''' save selected trays''' fname = QtGui.QFileDialog.getSaveFileName(self, 'Save selected color card images', '/home/chuong/Workspace/traitcapture-bin/unwarp_rectify/data') medianWidth, medianHeight = utils.getMedianRectSize(self.colorcardList) rectifiedColorcardImages = utils.rectifyRectImages(self.image, self.colorcardList, MedianSize = [medianWidth, medianHeight]) for i,rectifiedImage in enumerate(rectifiedColorcardImages): cv2.imwrite(str(fname) %i, rectifiedImage) def savePipelineSettings(self): ''' save to pipeline setting file''' fname = QtGui.QFileDialog.getSaveFileName(self, 'Save selection to pipeline settings', '/home/chuong/Workspace/traitcapture-bin/unwarp_rectify/data') settingPath = os.path.dirname(str(fname)) settings = [] if self.ImageSize != None and self.CameraMatrix != None and self.DistCoefs != None: undistort = ['undistort', \ {'mess': 'perform optical undistortion', \ 'cameraMatrix': self.CameraMatrix.tolist(), \ 'distortCoefs': self.DistCoefs.tolist(), \ 'imageSize': list(self.ImageSize), 'rotationAngle': self.rotationAngle } \ ] else: undistort = ['undistort', {'mess': '---skip optical undistortion---'}] settings.append(undistort) if len(self.colorcardList) > 0: medianSize = utils.getMedianRectSize(self.colorcardList) capturedColorcards = utils.rectifyRectImages(self.image, self.colorcardList, medianSize) colorCardFile = 'CapturedColorcard.png' cv2.imwrite(os.path.join(settingPath, colorCardFile), capturedColorcards[0][:,:,::-1].astype(np.uint8)) colorcardColors, colorStd = utils.getColorcardColors(capturedColorcards[0], GridSize = [6,4]) colorcardPosition, Width, Height, Angle = utils.getRectangleParamters(self.colorcardList[0]) colorcarddetect = ['colorcarddetect', \ {'mess': '---perform color card detection---', \ 'colorcardFile': colorCardFile,\ 'colorcardPosition': colorcardPosition.tolist(),\ 'colorcardTrueColors': utils.CameraTrax_24ColorCard } ] else: colorcarddetect = ['colorcarddetect', {'mess': '---skip color card detection---'}] settings.append(colorcarddetect) colorcorrect = ['colorcorrect', {'mess': '---perform color correction---'}] settings.append(colorcorrect) if len(self.trayList) > 0: trayMedianSize = utils.getMedianRectSize(self.trayList) trayImages = utils.rectifyRectImages(self.image, self.trayList, trayMedianSize) colorcardColors, colorStd = utils.getColorcardColors(capturedColorcards[0], GridSize = [6,4]) trayDict = {'mess': '---perform tray detection---'} trayDict['trayNumber'] = len(self.trayList) trayDict['trayFiles'] = 'Tray_%02d.png' trayPositions = [] for i,tray in enumerate(trayImages): cv2.imwrite(os.path.join(settingPath, trayDict['trayFiles'] %i), tray[:,:,::-1].astype(np.uint8)) trayPosition, Width, Height, Angle = utils.getRectangleParamters(self.trayList[i]) trayPositions.append(trayPosition.tolist()) trayDict['trayPositions'] = trayPositions traydetect = ['traydetect', trayDict] else: traydetect = ['traydetect', {'mess': '---skip tray detection---'}] settings.append(traydetect) if len(self.potList) > 0: trayMedianSize = utils.getMedianRectSize(self.trayList) potPosition, Width, Height, Angle = utils.getRectangleParamters(self.potList[0]) Width, Height = int(Width), int(Height) topLeft = [int(self.potList[0][0][0]), int(self.potList[0][0][1])] self.potImage = self.image[topLeft[1]:topLeft[1]+Height, topLeft[0]:topLeft[0]+Width, :] potFile = 'Pot.png' cv2.imwrite(os.path.join(settingPath, potFile), self.potImage[:,:,::-1].astype(np.uint8)) potDict = {'mess': '---perform pot detection---'} potDict['potPosition'] = potPosition.tolist() potDict['potSize'] = [int(Width), int(Height)] potDict['traySize'] = [int(trayMedianSize[0]), int(trayMedianSize[1])] potDict['potFile'] = potFile if self.potTemplate != None: potTemplateFile = 'potTemplate.png' cv2.imwrite(os.path.join(settingPath, potTemplateFile), self.potTemplate[:,:,::-1]) potDict['potTemplateFile'] = potTemplateFile potdetect = ['potdetect', potDict] else: potdetect = ['potdetect', {'mess': '---skip pot detection---'}] settings.append(potdetect) plantextract = ['plantextract', {'mess': '---perfrom plant biometrics extraction---'}] settings.append(plantextract) with open(fname, 'w') as outfile: outfile.write( yaml.dump(settings, default_flow_style=None) ) def rotateImage90Degrees(self): if self.image == None: self.status.append('No image to rotate.') return self.rotationAngle = self.rotationAngle + 90 if self.rotationAngle >= 360: self.rotationAngle = self.rotationAngle - 360 self.image = np.rot90(self.image) #.astype(uint8) self.status.append('Rot. angle = %d deg' %self.rotationAngle) if self.plotImg == None: self.plotImg = self.ax.imshow(self.image) else: self.plotImg.set_data(self.image) self.canvas.draw() def onMouseClicked(self, event): if self.panMode or self.zoomMode: return print('click', event.button, event.xdata, event.ydata) if event.button == 1 and event.xdata != None and event.ydata != None: self.leftClicks.append([event.xdata, event.ydata]) print('self.leftClicks =', self.leftClicks) Rect = [] AspectRatio = None if self.trayRadioButton.isChecked(): AspectRatio = self.trayAspectRatio elif self.colorcardRadioButton.isChecked(): AspectRatio = self.colorcardAspectRatio elif self.potRadioButton.isChecked(): AspectRatio = self.potAspectRatio if len(self.leftClicks) == 2 and AspectRatio != None: if self.potRadioButton.isChecked(): Rect = utils.getRectCornersFrom2Points(self.image, self.leftClicks, AspectRatio, Rounded = True) else: Rect = utils.getRectCornersFrom2Points(self.image, self.leftClicks, AspectRatio) self.leftClicks = [] elif len(self.leftClicks) == 4: Rect = [[x,y] for x,y in self.leftClicks] Rect = utils.correctPointOrder(Rect) self.leftClicks = [] if len(Rect) > 0: if self.trayRadioButton.isChecked(): self.trayList.append(Rect) self.status.append('Added tray selection.') elif self.colorcardRadioButton.isChecked(): self.colorcardList.append(Rect) self.status.append('Added color card selection.') else: self.potList.append(Rect) self.status.append('Added pot selection.') self.updateFigure() elif event.button == 3: # remove the last selection if len(self.leftClicks) > 0: self.leftClicks = self.leftClicks[:-1] self.status.append('Removed a previous click') else: if self.trayRadioButton.isChecked() and len(self.trayList) > 0: self.trayList = self.trayList[:-1] self.status.append('Removed a previous tray selection') elif self.colorcardRadioButton.isChecked() and len(self.colorcardList) > 0: self.colorcardList = self.colorcardList[:-1] self.status.append('Removed a previous color card selection.') elif self.potRadioButton.isChecked() and len(self.potList) > 0: self.potList = self.potList[:-1] self.status.append('Removed a previous pot selection') self.updateFigure() else: print('Ignored click') def onMouseMoves(self, event): if event.inaxes == self.ax: self.mousePosition.setText('x=%d, y=%d' %(event.xdata, event.ydata)) # self.crosshair = [event.xdata, event.ydata] else: self.mousePosition.setText('') # self.crosshair = None # self.updateFigure() def keyPressEvent(self, e): if e.key() == QtCore.Qt.Key_Escape: self.close() def closeEvent(self, event): quit_msg = "Are you sure you want to exit the program?" reply = QtGui.QMessageBox.question(self, 'Message', quit_msg, QtGui.QMessageBox.Yes, QtGui.QMessageBox.No) if reply == QtGui.QMessageBox.Yes: event.accept() else: event.ignore()