def getExistingDirectory(self, **kwargs): kwargs = self._processOpenKwargs(kwargs) fname = QtWidgets.QFileDialog.getExistingDirectory(**kwargs) if fname: p = PathStr(fname) self.opts['open'] = p.dirname() return p
def getSaveFileName(self, *args, **kwargs): """ analogue to QtWidgets.QFileDialog.getSaveFileNameAndFilter but returns the filename + chosen file ending even if not typed in gui """ if 'directory' not in kwargs: if self.opts['save']: if self.opts['save']: kwargs['directory'] = self.opts['save'] fname = QtWidgets.QFileDialog.getSaveFileName(**kwargs) if fname: if type(fname) == tuple: #only happened since qt5 #getSaveFileName returns (path, ftype) if not fname[0]: return p = PathStr(fname[0]) if not p.filetype(): ftyp = self._extractFtype(fname[1]) p = p.setFiletype(ftyp) else: p = PathStr(fname) self.opts['save'] = p.dirname() if self.opts['open'] is None: self.opts['open'] = self.opts['save'] return p
def _updateOpenRecentMenu(self): self.m_open_recent.clear() for s in self.app.session.app_opts['recent sessions']: s = PathStr(s) a = self.m_open_recent.addAction(s.basename()) a.setToolTip(s) a.triggered.connect(lambda checked, s=s: self.app.session.new(s))
def getOpenFileName(self, **kwargs): kwargs = self._processOpenKwargs(kwargs) fname = QtWidgets.QFileDialog.getOpenFileName(**kwargs) if isinstance(fname, tuple): fname = fname[0] if fname: p = PathStr(fname) self.opts['open'] = p.dirname() return p
def _getLayerLabel(fname, label): if fname is None: if label is None: return 'unknown' return label if PathStr(fname).isfile(): fname = PathStr(fname).basename() if label is not None: return '%s - %s' % (label, fname) return fname
def getOpenFileNames(self, **kwargs): kwargs = self._processOpenKwargs(kwargs) fnames = QtWidgets.QFileDialog.getOpenFileNames(**kwargs) # PyQt4 and 5 comp. workaround if isinstance(fnames, tuple) and isinstance(fnames[0], list): fnames = fnames[0] for n, f in enumerate(fnames): fnames[n] = PathStr(f) if fnames: self.opts['open'] = PathStr(f).dirname() return fnames
def pathJoin(pathlist): if len(pathlist) > 5: mod, number, name, current, date = pathlist[:5] date = date.replace(':', '-') dd0 = '__'.join((number, name)) dd1 = '__'.join((current, date)) out = PathStr(mod).join(dd0, dd1) if len(pathlist[5:]): out = out.join(*tuple(pathlist[5:])) return out return PathStr(pathlist[0]).join(*pathlist[1:])
def checkFolder(self): ''' Check for new files/folders in self.opts['folder'] and import them into dataArtist ''' fo = PathStr(self.opts['folder']) o = self.opts['files only'] files = fo.listdir() for f in files: if f not in self._files: ff = fo.join(f) if not o or ff.isfile(): self.gui.addFilePath(ff) self._files = files
def pathJoin(pathlist): if len(pathlist) > 5: mod, number, name, current, date = pathlist[:5] # date = date.replace(':', '-') date = datetime.strptime(date, '%x %X').strftime("%Y-%m-%dT%H_%M_%S") dd0 = '__'.join((number, name)) dd1 = '__'.join((current, date)) out = PathStr(mod).join(dd0, dd1) if len(pathlist[5:]): out = out.join(*tuple(pathlist[5:])) return out return PathStr(pathlist[0]).join(*pathlist[1:])
def _inspectArguments(self, args): """inspect the command-line-args and give them to appBase""" if args: self.exec_path = PathStr(args[0]) else: self.exec_path = None session_name = None args = args[1:] openSession = False for arg in args: if arg in ('-h', '--help'): self._showHelp() elif arg in ('-d', '--debug'): print('RUNNGING IN DEBUG-MODE') self.opts['debugMode'] = True elif arg in ('-l', '--log'): print('CREATE LOG') self.opts['createLog'] = True elif arg in ('-s', '--server'): self.opts['server'] = True elif arg in ('-o', '--open'): openSession = True elif openSession: session_name = arg else: print("Argument '%s' not known." % arg) return self._showHelp() return session_name
def _changeRootPath(self): r = self.gui.root # self.fileTableView.rootPath() new_r = QtWidgets.QFileDialog.getExistingDirectory(directory=r) if new_r: self.gui.setProjectFolder(PathStr(new_r)) self.fileTableView.rootPathChanged(False) # (PathStr(new_r)) self._updateFilePathLabel()
def setSessionPath(self, path, statename=None): if path: # and path.endswith('.%s' %self.FTYPE): # this script was opened out from a zip-container (named as # '*.pyz') self.path = PathStr(path) self.dir = self.path.dirname().abspath() # extract the zip temporally ZipFile(self.path, 'r').extractall(path=self.tmp_dir_session) self.n_sessions = len(self.stateNames()) # SET STATE snames = self.stateNames() if statename is None: # last one self.current_session = snames[-1] elif statename in snames: self.current_session = statename else: raise Exception( "state '%s' not in saved states %s" % (statename, snames)) else: self.path = None self.n_sessions = 0 self.current_session = None
def _pathToData(self, rootpath): '''returns 2d array of all files in [rootpath], splitted by folder''' self._root = rootpath f = list(PathStr(rootpath).nestedFiles(includeroot=False)) f = [fi for fi in f if not fi.isHidden()] data = [self.pathSplit(fi) for fi in f] dates = [dateStr(rootpath.join(fi).date()) for fi in f] sizes = [fs.toStr(rootpath.join(fi).size()) for fi in f] # transform list of lists into array: # lmax = max([len(d) for d in data]) data2 = np.zeros(shape=(len(data), self.columnCount()), dtype="<U%i" % self.STRING_LEN) pathindices = np.array([self.col(ni) for ni in range(self._n)]) fileindex = self.col(self._n) sizeindex = self.col(self._n + 3) dateindex = self.col(self._n + 1) for (dd, d2, date, size) in zip(data, data2, dates, sizes): # if 'DATA' in dd: # print(dd) d2[pathindices[:len(dd) - 1]] = dd[:-1] d2[fileindex] = dd[-1] d2[sizeindex] = size d2[dateindex] = date return data2
def fillFromState(self, lines, appendRows=False): self._allowModelToModifyCells = False if appendRows: row0 = self.rowCount() else: row0 = 0 self.clearContents() self.paths = [] nrows = row0 + len(lines) self.setRowCount(nrows) self._new_rows = [] for row, line in enumerate(lines): row += row0 for col, txt in enumerate(line): if txt != '': self.setCell(row, col, txt) item = self.item(row, 0) if len(line) and item: self.paths.append(PathStr(item.text())) self._setPathItem(row) self._setRowItem(row) self._new_rows.append(row) else: nrows -= 1 self.drawWidget.setExamplePath(self.paths[0]) self.setRowCount(nrows) self._checkShowOptionsColumn() self._new_paths = self.paths[row0:] self.filled.emit()
def addFilePath(self, filepath): ''' create a new display for one ore more given file paths INPUT: "Path/To/File.txt" ''' if filepath: return self.currentWorkspace().addFiles([PathStr(filepath)])
def vignettingFromRandomSteps(imgs, bg, inPlane_scale_factor=None, debugFolder=None, **kwargs): ''' important: first image should shown most iof the device because it is used as reference ''' # TODO: inPlane_scale_factor if debugFolder: debugFolder = PathStr(debugFolder) s = ObjectVignettingSeparation(imgs[0], bg, **kwargs) for img in imgs[1:]: fit = s.addImg(img) if debugFolder and fit is not False: imwrite(debugFolder.join('fit_%s.tiff' % len(s.fits)), fit) if debugFolder: imwrite(debugFolder.join('init.tiff'), s.flatField) smoothed_ff, mask, flatField, obj = s.separate() if debugFolder: imwrite(debugFolder.join('object.tiff'), obj) imwrite(debugFolder.join('flatfield.tiff'), flatField, dtype=float) imwrite(debugFolder.join('flatfield_smoothed.tiff'), smoothed_ff, dtype=float) return smoothed_ff, mask
def html2data(html): ''' extract either tables or images from html code ''' paths = [] # data = [] doc = lxml.html.fromstring(html) #images in html for img in doc.cssselect("img"): #doc.xpath('img'): # get the scr-path of the image: imgsrc = img.get('src') fname = PathStr(imgsrc).basename() if not hasattr(html2data, 'TMP_IMG_DIR'): html2data.TMP_IMG_DIR = PathStr(tempfile.mkdtemp('tmpImgDir')) fpath = html2data.TMP_IMG_DIR.join(fname) #in case img src is within HTML code: if not fname.filetype(): ftype = imgsrc[imgsrc.index('image/') + 6:imgsrc.index(';')] fpath = fpath.setFiletype(ftype) # download the image in a temporary folder: urllib.request.urlretrieve(imgsrc, fpath) paths.append(fpath) #TODO: doesnt work and table import from internet #...is not really needed # # tables # table = _html2PyTable(doc) # if table: # data.append(table) return paths #, data # def _html2PyTable(doc): # table = [] # try: # rows = doc.cssselect("tr") # # TODO: # except: # lxml.cccselect uses __import__ which doesnt work with pyinstaller # print('dynamic import error in lxml.cccselect') # return [] # for row in rows: # table.append(list()) # for td in row.cssselect("td"): # table[-1].append(str(td.text_content())) # return table
def getSaveFileName(self, *args, **kwargs): """ analogue to QtWidgets.QFileDialog.getSaveFileNameAndFilter but returns the filename + chosen file ending even if not typed in gui """ if 'directory' not in kwargs: if self.opts['save']: if self.opts['save']: kwargs['directory'] = self.opts['save'] fname = QtWidgets.QFileDialog.getSaveFileName(**kwargs) if fname: if type(fname) == tuple: # only happened since qt5 # getSaveFileName returns (path, ftype) if not fname[0]: return p = PathStr(fname[0]) if not p.filetype(): ftyp = self._extractFtype(fname[1]) p = p.setFiletype(ftyp) else: p = PathStr(fname) self.opts['save'] = p.dirname() if self.opts['open'] is None: self.opts['open'] = self.opts['save'] return p
def _copyToNewFolder(self): f = QtWidgets.QFileDialog.getExistingDirectory() if f: m = self.selectionModel() for index in m.selectedRows(): y = index.row() path = self._path2(y) self._root.join(path).copy(PathStr(f).join(path.basename())) QtGui.QDesktopServices.openUrl(QtCore.QUrl(f))
def plotFitResult(fit, show_legend=True, show_plots=True, save_to_file=False, foldername='', filename='', filetype='png'): from matplotlib import pyplot xvals = fit.xvals yvals = fit.yvals fit = fit.fitValues(xvals) fig, ax = pyplot.subplots(1) ax.plot(xvals, yvals, label='histogram', linewidth=3) for n, f in enumerate(fit): ax.plot(xvals, f, label='peak %i' % (n + 1), linewidth=6) l2 = ax.legend(loc='upper center', bbox_to_anchor=(0.7, 1.05), ncol=3, fancybox=True, shadow=True) l2.set_visible(show_legend) pyplot.xlabel('pixel value') pyplot.ylabel('number of pixels') if save_to_file: p = PathStr(foldername).join(filename).setFiletype(filetype) pyplot.savefig(p) with open(PathStr(foldername).join('%s_params.csv' % filename), 'w') as f: f.write('#x, #y, #fit\n') for n, (x, y, ys) in enumerate(zip(xvals, yvals)): fstr = ', '.join(str(f[n]) for f in fit) f.write('%s, %s, %s\n' % (x, y, fstr)) if show_plots: pyplot.show()
def valsFromPath(self): colDate = MATRIX_HEADER.index('Date') for row in range(self.rowCount()): if self._allowModelToModifyCells: path = PathStr(self.item(row, 0).text()) _success, entries = self.drawWidget.model(path) for col, e in enumerate(entries): col += 1 # if e == '': # item = self.item(row, col) # if hasattr(item, 'metaText'): # e = self.item(row, col).metaText if e != '': self.setCell(row, col, str(e)) # add date from file date is not already given: if not self.itemText(row, colDate): self.setCell(row, colDate, datetime.fromtimestamp( path.date()).strftime('%Y/%m/%d %H:%M:%S'))
def start(self): ''' configure the server and check the message-inbox every self.opts['refeshrate'] ''' self._files = PathStr(self.opts['folder']).listdir() self.timer = QtCore.QTimer() self.timer.timeout.connect(self.checkFolder) self.timer.start(self.opts['refreshrate'])
def _getFilePathsFromUrls(self, urls): ''' return a list of all file paths in event.mimeData.urls() ''' l = [] def _appendRecursive(path): if path.isfile(): #file l.append(path) else: for f in path: #for all files in folder _appendRecursive(path.join(f)) # one or more files/folders are dropped for url in urls: if url.isLocalFile(): path = PathStr(url.toLocalFile().toLocal8Bit().data()) if path.exists(): _appendRecursive(path) return l
def _docopyToNewFolder(self): self.setEnabled(True) f = self._tempF m = self.selectionModel() for index in m.selectedRows(): y = index.row() path = self._path2(y) self._root.join(path).copy(PathStr(f).join(path.basename())) QtGui.QDesktopServices.openUrl(QtCore.QUrl(f)) self._downloadDone = self._downloadDone_backup del self._tempF del self._downloadDone_backup
def equalizeImage(img, save_path=None, name_additive='_eqHist'): ''' Equalize the histogram (contrast) of an image works with RGB/multi-channel images and flat-arrays @param img - image_path or np.array @param save_path if given output images will be saved there @param name_additive if given this additive will be appended to output images @return output images if input images are numpy.arrays and no save_path is given @return None elsewise ''' if isinstance(img, basestring): img = PathStr(img) if not img.exists(): raise Exception("image path doesn't exist") img_name = img.basename().replace('.','%s.' %name_additive) if save_path is None: save_path = img.dirname() img = cv2.imread(img) if img.dtype != np.dtype('uint8'): #openCV cannot work with float arrays or uint > 8bit eqFn = _equalizeHistogram else: eqFn = cv2.equalizeHist if len(img.shape) == 3:#multi channel img like rgb for i in range(img.shape[2]): img[:, :, i] = eqFn(img[:, :, i]) else: # grey scale image img = eqFn(img) if save_path: img_name = PathStr(save_path).join(img_name) cv2.imwrite(img_name, img) return img
def _getDockTitle(title, number, names): ''' create a title for this display dock names -> list instances of PathStr ''' docktitle = '[%s] ' % number if title: docktitle += title if len(names) > 1: #if multiple files imported: #FOLDER IN TITLE dirname = PathStr(names[0]).dirname() if len(dirname) > 20: dirname = '~' + dirname[-20:] docktitle += "%s files from %s" % (len(names), dirname) elif names and names != [None]: #FILENAME IN TITLE name = PathStr(names[0]).basename() if len(name) > 20: name = name[:8] + '(...)' + name[-8:] docktitle += name return docktitle
def _getFilePathsFromUrls(self, urls): ''' return a list of all file paths in event.mimeData.urls() ''' limg, lagenda = [], [] def _appendRecursive(path): if path.isfile(): if path.filetype() in IMG_FILETYPES: limg.append(path) elif path.filetype() == 'csv': lagenda.append(path) else: for f in path: # for all files in folder _appendRecursive(path.join(f)) # one or more files/folders are dropped for url in urls: if url.isLocalFile(): path = PathStr(url.toLocalFile()) if path.exists(): _appendRecursive(path) return limg, lagenda
def __call__(self, path, prevFn=None, nextFn=None): ftype = PathStr(path).filetype() V = None if ftype != 'svg': if ftype in QtGui.QImageReader.supportedImageFormats(): V = InlineView_image elif ftype == 'csv': V = _CSVviewer elif ftype == 'txt': V = _Txtviewer if V is None: return os.startfile(path) self._prep(V, path, prevFn, nextFn) return V
def pathSplit(path): names = PathStr.splitNames(path) if len(names) == 4: mod = names[0] rest = names[3:] number, name = names[1].split('__') current, date = names[2].split('__') # 2016-10-04T11_06_45 -> 04/10/16 11:06:45 # TODO: return localized result date = datetime.strptime(date, "%Y-%m-%dT%H_%M_%S").strftime('%x %X') names = [mod, number, name, current, date] names.extend(rest) return names
def pathSplit(path): names = PathStr.splitNames(path) if len(names) == 4: mod = names[0] rest = names[3:] # print(names) # try: number, name = names[1].split('__') current, date = names[2].split('__') date = date.replace('-', ':') # except ValueError: # date = names[-2] # number, name = '', '' # print(mod, number, name, date, 8888888888888, # names, 555, names[1].split('__')) names = [mod, number, name, current, date] names.extend(rest) return names
def __init__(self, treeView): QtWidgets.QWidget.__init__(self) self._treeView = treeView self._menu = QtWidgets.QMenu(self) d = PathStr.getcwd() iconpath = os.path.join(d, 'media', 'icons', 'approve.svg') self._actionStart = QtWidgets.QAction(QtGui.QIcon(iconpath), 'Start', self._treeView, triggered=self._treeView.openProject) iconpath = os.path.join(d, 'media', 'icons', 'delete.svg') delete = QtWidgets.QAction(QtGui.QIcon(iconpath), 'Delete', self._treeView, triggered=self._treeView.deleteSelected) iconpath = os.path.join(d, 'media', 'icons', 'rename.svg') rename = QtWidgets.QAction(QtGui.QIcon(iconpath), 'Rename', self._treeView, triggered=self._treeView.editSelected) iconpath = os.path.join(d, 'media', 'icons', 'new.svg') newDir = QtWidgets.QAction(QtGui.QIcon(iconpath), 'New Directory', self._treeView, triggered=self._treeView.newDirInSelected) iconpath = os.path.join(d, 'media', 'icons', 'findReplace.svg') self._editStartScript = QtWidgets.QAction(QtGui.QIcon(iconpath), 'Edit start script', self._treeView, triggered=self._treeView.editStartScriptInSelected) iconpath = os.path.join(d, 'media', 'icons', 'bug.png') self._actionInDebugMode = QtWidgets.QAction(QtGui.QIcon(iconpath), 'Run in debug mode', self._treeView, triggered=self._treeView.runInDebugMode) self._menu.addAction(self._actionStart) self._menu.addAction(rename) self._menu.addAction(newDir) self._menu.addAction(self._editStartScript) self._menu.addAction(delete) self._menu.addAction(self._actionInDebugMode)
def html2data(html): ''' extract either tables or images from html code ''' paths = [] data = [] doc = lxml.html.fromstring(html) #images in html for img in doc.xpath('img'): #get the scr-path of the image: imgsrc = img.get('src') fname = PathStr(imgsrc).basename() fpath = TMP_IMG_DIR.join(fname) #download the image in a temporary folder: urllib.urlretrieve(imgsrc, fpath) paths.append(fpath) #tables table = _html2PyTable(doc) if table: data.append(table) return paths, data
def model(self, path): ''' depending on adjusted label positions, split given path into MATRIX row ''' names = path.splitNames()[-self.N_LAST_FOLDERS:] names[-1] = PathStr(names[-1]).rmFileType() out = [''] * (len(MATRIX_HEADER) - 1) success = True for i, n in enumerate(names): item = self._lGrid.itemAtPosition(1, (2 * i) + 1) if item: txt = item.widget().text() try: fn = CAT_FUNCTIONS[txt] fn(out, n) except KeyError: toRow(out, parsePath(n, txt)) except IndexError: pass return success, out
def equalizeImage(img, save_path=None, name_additive='_eqHist'): ''' Equalize the histogram (contrast) of an image works with RGB/multi-channel images and flat-arrays @param img - image_path or np.array @param save_path if given output images will be saved there @param name_additive if given this additive will be appended to output images @return output images if input images are numpy.arrays and no save_path is given @return None elsewise ''' if isinstance(img, string_types): img = PathStr(img) if not img.exists(): raise Exception("image path doesn't exist") img_name = img.basename().replace('.', '%s.' % name_additive) if save_path is None: save_path = img.dirname() img = cv2.imread(img) if img.dtype != np.dtype('uint8'): # openCV cannot work with float arrays or uint > 8bit eqFn = _equalizeHistogram else: eqFn = cv2.equalizeHist if len(img.shape) == 3: # multi channel img like rgb for i in range(img.shape[2]): img[:, :, i] = eqFn(img[:, :, i]) else: # grey scale image img = eqFn(img) if save_path: img_name = PathStr(save_path).join(img_name) cv2.imwrite(img_name, img) return img
mx = mn img /= mx img = exposure.equalize_hist(img, nbins=nBins) img *= mx if intType: img = img.astype(intType) return img if __name__ == '__main__': import sys import pylab as plt import imgProcessor from imgProcessor.imgIO import imread img = imread(PathStr(imgProcessor.__file__).dirname().join( 'media', 'electroluminescence', 'EL_module_orig.PNG')) eq = equalizeImage(img.copy()) if 'no_window' not in sys.argv: plt.figure('original') plt.imshow(img) plt.figure('equalised histogram') plt.imshow(eq) plt.show()
from dataArtist.widgets.StatusBar import StatusBar ########## #to allow to execute py code from a frozen environment #type e.g. gui.exe -exec print(4+4) if '-exec' in sys.argv: try: exec(sys.argv[-1]) except Exception, err: raw_input('-exec failed! --> %s' %err) sys.exit() ########## MEDIA_FOLDER = PathStr(dataArtist.__file__).dirname().join('media') HELP_FILE = MEDIA_FOLDER.join('USER_MANUAL.pdf') class Gui(MultiWorkspaceWindow): ''' The main class to be called to create an instance of dataArtist ''' def __init__(self, title='dataArtist'): MultiWorkspaceWindow.__init__(self, Workspace, title) s = self.app.session self.resize(600,550) #ALLOW DRAGnDROP self.setAcceptDrops(True)
def __init__(self, args, **kwargs): """ Args: first_start_dialog (Optional[bool]): Show a different dialog for the first start. name (Optional[str]): The applications name. type (Optional[str]): The file type to be used for saving sessions. icon (Optional[str]): Path to the application icon. """ QtCore.QObject.__init__(self) # SESSION CONSTANTS: self.NAME = kwargs.get('name', __main__.__name__) self.FTYPE = kwargs.get('ftype', 'pyz') self.ICON = kwargs.get('icon', None) # hidden app-preferences folder: self.dir = PathStr.home().mkdir('.%s' % self.NAME) self.APP_CONFIG_FILE = self.dir.join('config.txt') self._tmp_dir_session = None # session specific options: self.opts = _Opts({ 'maxSessions': 3, 'enableGuiIcons': True, 'writeToShell': True, 'createLog': False, 'debugMode': False, 'autosave': False, 'autosaveIntervalMin': 15, 'server': False, }, self) # global options - same for all new and restored sessions: self.app_opts = {'showCloseDialog': True, 'recent sessions': []} if not self.APP_CONFIG_FILE.exists(): # allow different first start dialog: dialog = kwargs.get('first_start_dialog', FirstStart) f = dialog(self) f.exec_() if not f.result(): sys.exit() # create the config file with open(self.APP_CONFIG_FILE, 'w') as f: pass else: with open(self.APP_CONFIG_FILE, 'r') as f: r = f.read() if r: self.app_opts.update(eval(r)) self._icons_enabled = False self.log_file = None dirname = self.app_opts['recent sessions'] if dirname: dirname = PathStr(dirname[-1]).dirname() self.dialogs = Dialogs(dirname) self.saveThread = _SaveThread() self._createdAutosaveFile = None self.tmp_dir_save_session = None # a work-dir for temp. storage: # self.tmp_dir_work = PathStr(tempfile.mkdtemp('%s_work' % self.NAME)) pathName = self._inspectArguments(args) self.setSessionPath(pathName) if self.opts['createLog']: self._setupLogFile() # create connectable stdout and stderr signal: self.streamOut = StreamSignal('out') self.streamErr = StreamSignal('err') self._enableGuiIcons() # Auto-save timer: self.timerAutosave = QtCore.QTimer() self.timerAutosave.timeout.connect(self._autoSave) self.opts.activate() # first thing to do after start: QtCore.QTimer.singleShot(0, self.restoreCurrentState)
''' Quantitative ElectroLuminescence Analysis ''' from fancytools.os.PathStr import PathStr name = 'QELA' __version__ = '-' # time stamp, set by server during packaging __author__ = 'Karl Bedrich' __email__ = '*****@*****.**' __license__ = 'GPLv3' # MEDIA_PATH = PathStr.getcwd().join('client', 'media') #only works if # executed from main dir MEDIA_PATH = PathStr(__file__).dirname().join( 'media') # TODO: works in FROZEN? ICON = MEDIA_PATH.join('logo.svg') PATH = PathStr.home().mkdir(".%s" % name)
img = self.img if color is None: color = img.max()-1 for l in self._linesFromvertices(self.vertices.astype(int)): cv2.line(img, tuple(l[:2]), tuple(l[2:]), color, thickness=thickness) return img if __name__ == '__main__': import sys import pylab as plt from fancytools.os.PathStr import PathStr import imgProcessor p = PathStr(imgProcessor.__file__).dirname().join( 'media', 'electroluminescence') img = p.join('EL_module_orig.PNG') q = QuadDetection(img) img = q.drawVertices() img2 = p.join('EL_cell_cracked.png') q = QuadDetection(img2) img2 = q.drawVertices(thickness=10) if 'no_window' not in sys.argv: plt.figure('module') plt.imshow(img) plt.figure('cell') plt.imshow(img2)
''' height, width = self.coeffs['shape'] ux,uy = self.getDeflection(width, height) r = self.coeffs['reprojectionError']#is RMSE of imgPoint-projectedPoints #transform to standard uncertainty #we assume rectangular distribution: ux = ux/(2 * 3**0.5) uy = uy/(2 * 3**0.5) return (ux,r), (uy,r), () if __name__ == '__main__': from fancytools.os.PathStr import PathStr import sys folder = PathStr(imgProcessor.__file__).dirname().join( 'media', 'lens_distortion') imgs = folder.all() l = LensDistortion() l.calibrate(board_size=(4,11), method='Asymmetric circles', images=imgs, sensorSize_mm=(18,13.5) ) print l.getCoeffStr() img = l.drawChessboard() if 'no_window' not in sys.argv: cv2.imshow('chessboard', img) cv2.waitKey(0)
k = _mkConvKernel(ksize, orientations, image) out = np.empty(shape=(s0,s1,orientations)) for i in xrange(orientations): out[:,:,i] = convolve2d(image, k[i], mode='same' ) return out if __name__ == '__main__': import sys from imgProcessor.imgIO import imread import pylab as plt from fancytools.os.PathStr import PathStr import imgProcessor p = PathStr(imgProcessor.__file__).dirname().join( 'media', 'electroluminescence') img = imread(p.join('EL_cell_cracked.PNG'), 'gray') k = (15,15) o = 4 p0=(141,91) p1=(251,242) p2=(667,570) kernels = _mkConvKernel(k, o, img) h = hog(img, ksize=k, orientations=o) if 'no_window' not in sys.argv: plt.figure('image') plt.imshow(img)
from fancywidgets.pyQtBased.Dialogs import Dialogs # foreign from qtpy import QtGui, QtWidgets, QtCore, QtSvg # built-in import os from zipfile import ZipFile import distutils from distutils import spawn import subprocess import sys import tempfile CONFIG_FILE = PathStr.home().join(__name__) class Launcher(QtWidgets.QMainWindow): """ A graphical starter for *.pyz files created by the save-method from appbase.MainWindow NEEDS AN OVERHAUL ... after that's done it will be able to: * show all *.pyz-files in a filetree * show the session specific ... * icon * description * author etc.
def __init__(self, title='PYZ-Launcher', icon=None, start_script=None, left_header=None, right_header=None, file_type='pyz' ): self.dialogs = Dialogs() _path = PathStr.getcwd() _default_text_color = '#3c3c3c' if icon is None: icon = os.path.join(_path, 'media', 'launcher_logo.svg') if start_script is None: start_script = os.path.join(_path, 'test_session.py') if left_header is None: _description = "<a href=%s style='color: %s'>%s</a>" % ( appbase.__url__, _default_text_color, appbase.__description__) left_header = """<b>%s</b><br> version <a href=%s style='color: %s'>%s</a><br> autor <a href=mailto:%s style='color: %s'>%s</a> """ % ( # text-decoration:underline _description, os.path.join(_path, 'media', 'recent_changes.txt'), _default_text_color, appbase.__version__, appbase.__email__, _default_text_color, appbase.__author__ ) if right_header is None: # if no header is given, list all pdfs in folder media as link d = _path right_header = '' for f in os.listdir(os.path.join(d, 'media')): if f.endswith('.pdf'): _guidePath = os.path.join(d, 'media', f) right_header += "<a href=%s style='color: %s'>%s</a><br>" % ( _guidePath, _default_text_color, f[:-4]) right_header = right_header[:-4] QtWidgets.QMainWindow.__init__(self) self._start_script = start_script self.setWindowTitle(title) self.setWindowIcon(QtGui.QIcon(icon)) self.resize(900, 500) # BASE STRUTURE area = QtWidgets.QWidget() self.setCentralWidget(area) layout = QtWidgets.QVBoxLayout() area.setLayout(layout) #header = QtWidgets.QHBoxLayout() # layout.addLayout(header) # grab the default text color of a qlabel to color all links from blue to it: # LEFT TEXT info = QtWidgets.QLabel(left_header) info.setOpenExternalLinks(True) # LOGO header = QtWidgets.QWidget() header.setFixedHeight(70) headerlayout = QtWidgets.QHBoxLayout() header.setLayout(headerlayout) logo = QtSvg.QSvgWidget(icon) logo.setFixedWidth(50) logo.setFixedHeight(50) headerlayout.addWidget(logo) headerlayout.addWidget(info) layout.addWidget(header) # RIGHT_HEADER userGuide = QtWidgets.QLabel(right_header) userGuide.setOpenExternalLinks(True) userGuide.setAlignment(QtCore.Qt.AlignTop | QtCore.Qt.AlignRight) headerlayout.addWidget(userGuide) # ROOT-PATH OF THE SESSIONS rootLayout = QtWidgets.QHBoxLayout() rootFrame = QtWidgets.QFrame() rootFrame.setFrameStyle( QtWidgets.QFrame.StyledPanel | QtWidgets.QFrame.Plain) rootFrame.setFixedHeight(45) rootFrame.setLineWidth(0) rootFrame.setLayout(rootLayout) layout.addWidget(rootFrame) self.rootDir = QtWidgets.QLabel() self.rootDir.setAutoFillBackground(True) self.rootDir.setStyleSheet("QLabel { background-color: white; }") # FILE-BROWSER self.treeView = _TreeView() self.fileSystemModel = _FileSystemModel(self.treeView, file_type) self.fileSystemModel.setNameFilters(['*.%s' % file_type]) self.fileSystemModel.setNameFilterDisables(False) self.treeView.setModel(self.fileSystemModel) treelayout = QtWidgets.QHBoxLayout() splitter = QtWidgets.QSplitter(QtCore.Qt.Orientation(1)) self.fileInfo = _PyzInfo(splitter, self.fileSystemModel, self.treeView) self.treeView.clicked.connect(self.fileInfo.update) splitter.addWidget(self.treeView) splitter.addWidget(self.fileInfo) treelayout.addWidget(splitter) layout.addLayout(treelayout) # get last root-path self._path = PathStr('') if CONFIG_FILE: try: self._path = PathStr( open( CONFIG_FILE, 'r').read().decode('unicode-escape')) except IOError: pass # file not existant if not self._path or not self._path.exists(): msgBox = QtWidgets.QMessageBox() msgBox.setText("Please choose your projectDirectory.") msgBox.exec_() self._changeRootDir() self.treeView.setPath(self._path) abspath = os.path.abspath(self._path) self.rootDir.setText(abspath) rootLayout.addWidget(self.rootDir) # GO UPWARDS ROOT-PATH BUTTON btnUpRootDir = QtWidgets.QPushButton('up') btnUpRootDir.clicked.connect(self._goUpRootDir) rootLayout.addWidget(btnUpRootDir) # DEFINE CURRENT DIR AS ROOT-PATH btnDefineRootDir = QtWidgets.QPushButton('set') btnDefineRootDir.clicked.connect(self._defineRootDir) rootLayout.addWidget(btnDefineRootDir) # SELECT ROOT-PATH BUTTON buttonRootDir = QtWidgets.QPushButton('select') buttonRootDir.clicked.connect(self._changeRootDir) rootLayout.addWidget(buttonRootDir) # NEW-BUTTON if self._start_script: newButton = QtWidgets.QPushButton('NEW') newButton.clicked.connect(self._openNew) layout.addWidget(newButton)
def rootDir(): try: return PathStr( open(CONFIG_FILE, 'r').read().decode('unicode-escape')) except IOError: # create starter return PathStr.home()
class Session(QtCore.QObject): """Session management to be accessible in QtWidgets.QApplication.instance().session * extract the opened (as pyz-zipped) session in a temp folder * create 2nd temp-folder for sessions to be saved * send a close signal to all child structures when exit * write a log file with all output * enable icons in menus of gnome-sessions [linux only] * gives option of debug mode """ # sigPathChanged = QtCore.Signal(object) #path sigSave = QtCore.Signal(object) # state dict sigRestore = QtCore.Signal(object) # state dict def __init__(self, args, **kwargs): """ Args: first_start_dialog (Optional[bool]): Show a different dialog for the first start. name (Optional[str]): The applications name. type (Optional[str]): The file type to be used for saving sessions. icon (Optional[str]): Path to the application icon. """ QtCore.QObject.__init__(self) # SESSION CONSTANTS: self.NAME = kwargs.get('name', __main__.__name__) self.FTYPE = kwargs.get('ftype', 'pyz') self.ICON = kwargs.get('icon', None) # hidden app-preferences folder: self.dir = PathStr.home().mkdir('.%s' % self.NAME) self.APP_CONFIG_FILE = self.dir.join('config.txt') self._tmp_dir_session = None # session specific options: self.opts = _Opts({ 'maxSessions': 3, 'enableGuiIcons': True, 'writeToShell': True, 'createLog': False, 'debugMode': False, 'autosave': False, 'autosaveIntervalMin': 15, 'server': False, }, self) # global options - same for all new and restored sessions: self.app_opts = {'showCloseDialog': True, 'recent sessions': []} if not self.APP_CONFIG_FILE.exists(): # allow different first start dialog: dialog = kwargs.get('first_start_dialog', FirstStart) f = dialog(self) f.exec_() if not f.result(): sys.exit() # create the config file with open(self.APP_CONFIG_FILE, 'w') as f: pass else: with open(self.APP_CONFIG_FILE, 'r') as f: r = f.read() if r: self.app_opts.update(eval(r)) self._icons_enabled = False self.log_file = None dirname = self.app_opts['recent sessions'] if dirname: dirname = PathStr(dirname[-1]).dirname() self.dialogs = Dialogs(dirname) self.saveThread = _SaveThread() self._createdAutosaveFile = None self.tmp_dir_save_session = None # a work-dir for temp. storage: # self.tmp_dir_work = PathStr(tempfile.mkdtemp('%s_work' % self.NAME)) pathName = self._inspectArguments(args) self.setSessionPath(pathName) if self.opts['createLog']: self._setupLogFile() # create connectable stdout and stderr signal: self.streamOut = StreamSignal('out') self.streamErr = StreamSignal('err') self._enableGuiIcons() # Auto-save timer: self.timerAutosave = QtCore.QTimer() self.timerAutosave.timeout.connect(self._autoSave) self.opts.activate() # first thing to do after start: QtCore.QTimer.singleShot(0, self.restoreCurrentState) def setSessionPath(self, path, statename=None): if path: # and path.endswith('.%s' %self.FTYPE): # this script was opened out from a zip-container (named as # '*.pyz') self.path = PathStr(path) self.dir = self.path.dirname().abspath() # extract the zip temporally ZipFile(self.path, 'r').extractall(path=self.tmp_dir_session) self.n_sessions = len(self.stateNames()) # SET STATE snames = self.stateNames() if statename is None: # last one self.current_session = snames[-1] elif statename in snames: self.current_session = statename else: raise Exception( "state '%s' not in saved states %s" % (statename, snames)) else: self.path = None self.n_sessions = 0 self.current_session = None def writeLog(self, write=True): if not self.log_file: return so = self.streamOut.message se = self.streamErr.message w = self.log_file.write if write: try: # ensure only connected once so.disconnect(w) se.disconnect(w) except TypeError: pass so.connect(w) se.connect(w) else: try: so.disconnect(w) se.disconnect(w) except TypeError: pass def _enableGuiIcons(self): # enable icons in all QMenuBars only for this program if generally # disabled if self.opts['enableGuiIcons']: if os.name == 'posix': # linux this_env = str(os.environ.get('DESKTOP_SESSION')) relevant_env = ( 'gnome', 'gnome-shell', 'ubuntustudio', 'xubuntu') if this_env in relevant_env: if 'false' in os.popen( # if the menu-icons on the gnome-desktop are disabled 'gconftool-2 --get /desktop/gnome/interface/menus_have_icons').read(): print('enable menu-icons') os.system( 'gconftool-2 --type Boolean --set /desktop/gnome/interface/menus_have_icons True') self._icons_enabled = True def _setupLogFile(self): lfile = self.tmp_dir_session.join('log.txt') if lfile.exists(): self.log_file = open(lfile, 'a') else: self.log_file = open(lfile, 'w') self.log_file.write(''' #################################### New run at %s #################################### ''' % strftime("%d.%m.%Y|%H:%M:%S", gmtime())) def checkMaxSessions(self, nMax=None): """ check whether max. number of saved sessions is reached if: remove the oldest session """ if nMax is None: nMax = self.opts['maxSessions'] l = self.stateNames() if len(l) > nMax: for f in l[:len(l) - nMax]: self.tmp_dir_session.remove(str(f)) def stateNames(self): """Returns: list: the names of all saved sessions """ if self.current_session: s = self.tmp_dir_session l = s.listdir() l = [x for x in l if s.join(x).isdir()] naturalSorting(l) else: l=[] # bring autosave to first position: if 'autoSave' in l: l.remove('autoSave') l.insert(0, 'autoSave') return l def restorePreviousState(self): s = self.stateNames() if s: i = s.index(self.current_session) if i > 1: self.current_session = s[i - 1] self.restoreCurrentState() def restoreNextState(self): s = self.stateNames() if s: i = s.index(self.current_session) if i < len(s) - 1: self.current_session = s[i + 1] self.restoreCurrentState() def restoreStateName(self, name): """restore the state of given [name]""" self.current_session = name self.restoreCurrentState() def renameState(self, oldStateName, newStateName): s = self.tmp_dir_session.join(oldStateName) s.rename(newStateName) if self.current_session == oldStateName: self.current_session = newStateName print("==> State [%s] renamed to [%s]" % (oldStateName, newStateName)) def _recusiveReplacePlaceholderWithArray(self, state, arrays): def recursive(state): for key, val in list(state.items()): if isinstance(val, dict): recursive(val) elif isinstance(val, str) and val.startswith('arr_'): state[key] = arrays[val] recursive(state) def restoreCurrentState(self): if self.current_session: orig = self.tmp_dir_save_session path = self.tmp_dir_save_session = self.tmp_dir_session.join( self.current_session) with open(path.join('state.pickle'), "rb") as f: state = pickle.load(f) p = path.join('arrays.npz') if p.exists(): arrays = np.load(path.join('arrays.npz')) self._recusiveReplacePlaceholderWithArray(state, arrays) self.dialogs.restoreState(state['dialogs']) self.opts.update(state['session']) self.sigRestore.emit(state) self.tmp_dir_save_session = orig print( "==> State [%s] restored from '%s'" % (self.current_session, self.path)) def addSession(self): self.current_session = self.n_sessions self.n_sessions += 1 self.tmp_dir_save_session = self.tmp_dir_session.join( str(self.n_sessions)).mkdir() self.checkMaxSessions() def quit(self): print('exiting...') # RESET ICONS if self._icons_enabled: print('disable menu-icons') os.system( # restore the standard-setting for seeing icons in the menus 'gconftool-2 --type Boolean --set /desktop/gnome/interface/menus_have_icons False') # WAIT FOR PROMT IF IN DEBUG MODE if self.opts['debugMode']: input("Press any key to end the session...") # REMOVE TEMP FOLDERS try: self.tmp_dir_session.remove() # self.tmp_dir_work.remove() except OSError: pass # in case the folders are used by another process with open(self.APP_CONFIG_FILE, 'w') as f: f.write(str(self.app_opts)) # CLOSE LOG FILE if self.log_file: self.writeLog(False) self.log_file.close() def _inspectArguments(self, args): """inspect the command-line-args and give them to appBase""" if args: self.exec_path = PathStr(args[0]) else: self.exec_path = None session_name = None args = args[1:] openSession = False for arg in args: if arg in ('-h', '--help'): self._showHelp() elif arg in ('-d', '--debug'): print('RUNNGING IN DEBUG-MODE') self.opts['debugMode'] = True elif arg in ('-l', '--log'): print('CREATE LOG') self.opts['createLog'] = True elif arg in ('-s', '--server'): self.opts['server'] = True elif arg in ('-o', '--open'): openSession = True elif openSession: session_name = arg else: print("Argument '%s' not known." % arg) return self._showHelp() return session_name def _showHelp(self): sys.exit(''' %s-sessions can started with the following arguments: [-h or --help] - show the help-page [-d or --debug] - run in debugging-mode [-l or --log] - create log file [-n or --new] - start a new session, don'l load saved properties [-exec [cmd]] - execute python code from this script/executable ''' % self.__class__.__name__) def save(self): """save the current session override, if session was saved earlier""" if self.path: self._saveState(self.path) else: self.saveAs() def saveAs(self, filename=None): if filename is None: # ask for filename: filename = self.dialogs.getSaveFileName(filter="*.%s" % self.FTYPE) if filename: self.path = filename self._saveState(self.path) if self._createdAutosaveFile: self._createdAutosaveFile.remove() print( "removed automatically created '%s'" % self._createdAutosaveFile) self._createdAutosaveFile = None def replace(self, path): """ replace current session with one given by file path """ self.setSessionPath(path) self.restoreCurrentState() def open(self): """open a session to define in a dialog in an extra window""" filename = self.dialogs.getOpenFileName(filter="*.%s" % self.FTYPE) if filename: self.new(filename) def new(self, filename=None): """start a session an independent process""" path = (self.exec_path,) if self.exec_path.filetype() in ('py', 'pyw', 'pyz', self.FTYPE): # get the absolute path to the python-executable p = find_executable("python") path = (p, 'python') + path else: # if run in frozen env (.exe): # first arg if execpath of the next session: path += (self.exec_path,) if filename: path += ('-o', filename) os.spawnl(os.P_NOWAIT, *path) def registerMainWindow(self, win): win.setWindowIcon(QtGui.QIcon(self.ICON)) self._mainWindow = win win.show = self._showMainWindow win.hide = self._hideMainWindow if self.opts['server']: server_ = Server(win) win.hide() else: win.show() @property def tmp_dir_session(self): #only create folder if needed if self._tmp_dir_session is None: # make temp-dir # the directory where the content of the *pyz-file will be copied: self._tmp_dir_session = PathStr( tempfile.mkdtemp( '%s_session' % self.NAME)) return self._tmp_dir_session def _showMainWindow(self): try: # restore autosave del self._autosave except AttributeError: pass self._mainWindow.__class__.show(self._mainWindow) def _hideMainWindow(self): # disable autosave on hidden window self._autosave = self.opts['autosave'] self.opts['autosave'] = False self._mainWindow.__class__.hide(self._mainWindow) def _saveState(self, path): """save current state and add a new state""" self.addSession() # next session self._save(str(self.n_sessions), path) def _autoSave(self): """save state into 'autosave' """ a = 'autoSave' path = self.path if not path: path = self.dir.join('%s.%s' % (a, self.FTYPE)) self._createdAutosaveFile = path self.tmp_dir_save_session = self.tmp_dir_session.join(a).mkdir() self._save(a, path) def blockingSave(self, path): """ saved session to file - returns after finish only called by interactiveTutorial-save at the moment """ self.tmp_dir_save_session = self.tmp_dir_session.join('block').mkdir() state = {'session': dict(self.opts), 'dialogs': self.dialogs.saveState()} self.saveThread.prepare('0', path, self.tmp_dir_session, state) self.sigSave.emit(self) self.saveThread.run() def _save(self, stateName, path): """save into 'stateName' to pyz-path""" print('saving...') state = {'session': dict(self.opts), 'dialogs': self.dialogs.saveState()} self.sigSave.emit(state) self.saveThread.prepare(stateName, path, self.tmp_dir_session, state) self.saveThread.start() self.current_session = stateName r = self.app_opts['recent sessions'] try: # is this session already exists: remove it r.pop(r.index(path)) except ValueError: pass # add this session at the beginning r.insert(0, path)
ymx = gy out[ii,jj]=np.nanmedian(arr[xmn:xmx:every, ymn:ymx:every]) jj += 1 ii += 1 return ii,jj if __name__ == '__main__': import sys import pylab as plt import imgProcessor from imgProcessor.imgIO import imread from fancytools.os.PathStr import PathStr p = PathStr(imgProcessor.__file__).dirname().join( 'media', 'electroluminescence') img = imread(p.join('EL_module_orig.PNG'), 'gray') #add nans img[300:310]=np.nan img[:,110:130]=np.nan bg = fastNaNmedianFilter(img,40,5) if 'no_window' not in sys.argv: plt.figure('image') plt.imshow(img, interpolation='none') plt.figure('background') plt.imshow(bg, interpolation='none')
if not overlapping: overlap = 0 return np.vstack((img1[0:sizex - overlap, :], inter, img2[overlap:, :])) if __name__ == '__main__': import sys from fancytools.os.PathStr import PathStr from imgProcessor.imgIO import imread import cv2 import imgProcessor d = PathStr( imgProcessor.__file__).dirname().join( 'media', 'electroluminescence') i1 = d.join('EL_module_a_dist.PNG') i2 = d.join('EL_module_b_dist.PNG') img1 = imread(i1) // 2 img2 = imread(i2) cv2.namedWindow("stitched") s0 = img1.shape[0] for i in range(-s0, s0): img = linearBlend(img1, img2, i) if 'no_window' not in sys.argv: cv2.imshow('stitched', img) cv2.waitKey(delay=20)
final_img = final_img_crop return final_img else: return self.base_img_rgb if __name__ == '__main__': import sys import timeit import imgProcessor from fancytools.os.PathStr import PathStr d = PathStr(imgProcessor.__file__).dirname().join('media') i1 = cv2.imread(d.join('peanut_1.jpg')) i2 = cv2.imread(d.join('peanut_2.jpg')) def fn(overlap=None): # stitch 2 images taken in 2 different perspectives together: p = PerspectiveImageStitching(i1) fn.result = p.addImg(i2, overlap=overlap) # lets find out which method is faster: print(('time needed without given overlap [s]: ', timeit.timeit(fn, number=1))) print(('time needed with given overlap [s]: ', timeit.timeit(lambda: fn(overlap=150), number=1))) if 'no_window' not in sys.argv:
image = _coarsenImage(image, fx) k = _kSizeFromStd(std) y0 = int(round(y0 / fy)) x0 = int(round(x0 / fx)) arr = image[y0 - k:y0 + k, x0 - k:x0 + k] U = positionToIntensityUncertainty(arr, std / fx, std / fx) return U[k:-k, k:-k] if __name__ == '__main__': import sys import imgProcessor from imgProcessor.imgIO import imread from fancytools.os.PathStr import PathStr from pylab import plt d = PathStr(imgProcessor.__file__).dirname().join( 'media', 'electroluminescence') # STITCH BOTTOM img = imread(d.join('EL_cell_cracked.png'), 'gray') img = img[::2, ::2] # speed up for this test case # CASE 1: constant position uncertainty: stdx = 9 stdy = 3 sint = positionToIntensityUncertainty(img, stdx, stdy) # CASE2: variable position uncertainty: # x,y 0...15 stdx2 = np.fromfunction(lambda x, y: x * y, img.shape) stdx2 /= stdx2[-1, -1] / 9
# coding=utf-8 ''' Count coded lines of all projects ''' from fancytools.utils.countLines import countLines from fancytools.os.PathStr import PathStr d = PathStr.getcwd().dirname() countLines([ d.join('dataArtist'), d.join('fancyTools'), d.join('fancyWidgets'), d.join('appBase'), d.join('imgprocessor'), # d.join('pvimgprocessor'), d.join('interactiveTutorial'), d.join('headerSetter') ], exclude_folders_containing=("DUMP", 'dist', 'dev', 'build', 'fancytools3'), exclude_blank_lines=True, exclude_commented_lines=True, count_only_py_files=True)
for jj in range(ymn, ymx): if mask2[ii, jj]: buff[n] = arr[ii, jj] n += 1 if n > 0: out[i, j] = np.median(buff[:n]) if __name__ == '__main__': import sys import pylab as plt import imgProcessor from imgProcessor.imgIO import imread from fancytools.os.PathStr import PathStr p = PathStr(imgProcessor.__file__).dirname().join( 'media', 'electroluminescence') img = imread(p.join('EL_module_orig.PNG'), 'gray', dtype=float) # add nans img[300:320] = np.nan img[:, 110:130] = np.nan mask = np.isnan(img) bg1 = maskedFilter(img.copy(), mask, 80, fn='mean') bg2 = maskedFilter(img.copy(), mask, 80, fn='median') img2 = maskedFilter(img.copy(), mask, 80, fill_mask=False, fn='mean') if 'no_window' not in sys.argv: